// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/automation/testing_automation_provider.h"

#include <map>
#include <set>
#include <string>
#include <vector>

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/json/string_escape.h"
#include "base/path_service.h"
#include "base/process.h"
#include "base/process_util.h"
#include "base/stringprintf.h"
#include "base/threading/thread_restrictions.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/autocomplete/autocomplete.h"
#include "chrome/browser/autocomplete/autocomplete_edit.h"
#include "chrome/browser/autocomplete/autocomplete_match.h"
#include "chrome/browser/autofill/autofill_manager.h"
#include "chrome/browser/autofill/credit_card.h"
#include "chrome/browser/autofill/personal_data_manager.h"
#include "chrome/browser/automation/automation_autocomplete_edit_tracker.h"
#include "chrome/browser/automation/automation_browser_tracker.h"
#include "chrome/browser/automation/automation_provider_json.h"
#include "chrome/browser/automation/automation_provider_list.h"
#include "chrome/browser/automation/automation_provider_observers.h"
#include "chrome/browser/automation/automation_tab_tracker.h"
#include "chrome/browser/automation/automation_util.h"
#include "chrome/browser/automation/automation_window_tracker.h"
#include "chrome/browser/automation/ui_controls.h"
#include "chrome/browser/blocked_content_container.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/bookmarks/bookmark_storage.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_shutdown.h"
#include "chrome/browser/debugger/devtools_manager.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/download_shelf.h"
#include "chrome/browser/download/save_package.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_updater.h"
#include "chrome/browser/history/top_sites.h"
#include "chrome/browser/importer/importer_host.h"
#include "chrome/browser/instant/instant_controller.h"
#include "chrome/browser/notifications/balloon.h"
#include "chrome/browser/notifications/balloon_collection.h"
#include "chrome/browser/notifications/notification.h"
#include "chrome/browser/notifications/notification_ui_manager.h"
#include "chrome/browser/password_manager/password_store.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_model.h"
#include "chrome/browser/tab_contents/confirm_infobar_delegate.h"
#include "chrome/browser/tab_contents/link_infobar_delegate.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/translate/translate_infobar_delegate.h"
#include "chrome/browser/translate/translate_tab_helper.h"
#include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog.h"
#include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h"
#include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/download/download_tab_helper.h"
#include "chrome/browser/ui/find_bar/find_bar.h"
#include "chrome/browser/ui/login/login_prompt.h"
#include "chrome/browser/ui/omnibox/location_bar.h"
#include "chrome/browser/ui/search_engines/keyword_editor_controller.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/browser/ui/webui/active_downloads_ui.h"
#include "chrome/browser/ui/webui/shown_sections_handler.h"
#include "chrome/common/automation_messages.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_extent.h"
#include "chrome/common/extensions/url_pattern.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/browser/renderer_host/render_process_host.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/interstitial_page.h"
#include "content/common/common_param_traits.h"
#include "content/common/notification_service.h"
#include "net/base/cookie_store.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
#include "ui/base/events.h"
#include "ui/base/message_box_flags.h"
#include "webkit/plugins/npapi/plugin_list.h"

namespace {

void SendMouseClick(int flags) {
  ui_controls::MouseButton button = ui_controls::LEFT;
  if ((flags & ui::EF_LEFT_BUTTON_DOWN) ==
      ui::EF_LEFT_BUTTON_DOWN) {
    button = ui_controls::LEFT;
  } else if ((flags & ui::EF_RIGHT_BUTTON_DOWN) ==
             ui::EF_RIGHT_BUTTON_DOWN) {
    button = ui_controls::RIGHT;
  } else if ((flags & ui::EF_MIDDLE_BUTTON_DOWN) ==
             ui::EF_MIDDLE_BUTTON_DOWN) {
    button = ui_controls::MIDDLE;
  } else {
    NOTREACHED();
  }

  ui_controls::SendMouseClick(button);
}

class AutomationInterstitialPage : public InterstitialPage {
 public:
  AutomationInterstitialPage(TabContents* tab,
                             const GURL& url,
                             const std::string& contents)
      : InterstitialPage(tab, true, url),
        contents_(contents) {
  }

  virtual std::string GetHTMLContents() { return contents_; }

 private:
  const std::string contents_;

  DISALLOW_COPY_AND_ASSIGN(AutomationInterstitialPage);
};

}  // namespace

TestingAutomationProvider::TestingAutomationProvider(Profile* profile)
    : AutomationProvider(profile),
#if defined(TOOLKIT_VIEWS)
      popup_menu_waiter_(NULL),
#endif
      redirect_query_(0) {
  BrowserList::AddObserver(this);
  registrar_.Add(this, NotificationType::SESSION_END,
                 NotificationService::AllSources());
}

TestingAutomationProvider::~TestingAutomationProvider() {
  BrowserList::RemoveObserver(this);
}

void TestingAutomationProvider::OnBrowserAdded(const Browser* browser) {
}

void TestingAutomationProvider::OnBrowserRemoved(const Browser* browser) {
  // For backwards compatibility with the testing automation interface, we
  // want the automation provider (and hence the process) to go away when the
  // last browser goes away.
  if (BrowserList::empty() && !CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kKeepAliveForTest)) {
    // If you change this, update Observer for NotificationType::SESSION_END
    // below.
    MessageLoop::current()->PostTask(FROM_HERE,
        NewRunnableMethod(this, &TestingAutomationProvider::OnRemoveProvider));
  }
}

void TestingAutomationProvider::OnSourceProfilesLoaded() {
  DCHECK_NE(static_cast<ImporterList*>(NULL), importer_list_.get());

  // Get the correct profile based on the browser that the user provided.
  importer::SourceProfile source_profile;
  size_t i = 0;
  size_t importers_count = importer_list_->count();
  for ( ; i < importers_count; ++i) {
    importer::SourceProfile profile = importer_list_->GetSourceProfileAt(i);
    if (profile.importer_name == import_settings_data_.browser_name) {
      source_profile = profile;
      break;
    }
  }
  // If we made it to the end of the loop, then the input was bad.
  if (i == importers_count) {
    AutomationJSONReply(this, import_settings_data_.reply_message)
        .SendError("Invalid browser name string found.");
    return;
  }

  scoped_refptr<ImporterHost> importer_host(new ImporterHost);
  importer_host->SetObserver(
      new AutomationProviderImportSettingsObserver(
          this, import_settings_data_.reply_message));

  Profile* target_profile = import_settings_data_.browser->profile();
  importer_host->StartImportSettings(source_profile,
                                     target_profile,
                                     import_settings_data_.import_items,
                                     new ProfileWriter(target_profile),
                                     import_settings_data_.first_run);
}

void TestingAutomationProvider::Observe(NotificationType type,
                                        const NotificationSource& source,
                                        const NotificationDetails& details) {
  DCHECK(type == NotificationType::SESSION_END);
  // OnBrowserRemoved does a ReleaseLater. When session end is received we exit
  // before the task runs resulting in this object not being deleted. This
  // Release balance out the Release scheduled by OnBrowserRemoved.
  Release();
}

bool TestingAutomationProvider::OnMessageReceived(
    const IPC::Message& message) {
  bool handled = true;
  bool deserialize_success = true;
  IPC_BEGIN_MESSAGE_MAP_EX(TestingAutomationProvider,
                           message,
                           deserialize_success)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_CloseBrowser, CloseBrowser)
    IPC_MESSAGE_HANDLER(AutomationMsg_CloseBrowserRequestAsync,
                        CloseBrowserAsync)
    IPC_MESSAGE_HANDLER(AutomationMsg_ActivateTab, ActivateTab)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_AppendTab, AppendTab)
    IPC_MESSAGE_HANDLER(AutomationMsg_ActiveTabIndex, GetActiveTabIndex)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_CloseTab, CloseTab)
    IPC_MESSAGE_HANDLER(AutomationMsg_GetCookies, GetCookies)
    IPC_MESSAGE_HANDLER(AutomationMsg_SetCookie, SetCookie)
    IPC_MESSAGE_HANDLER(AutomationMsg_DeleteCookie, DeleteCookie)
    IPC_MESSAGE_HANDLER(AutomationMsg_ShowCollectedCookiesDialog,
                        ShowCollectedCookiesDialog)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(
        AutomationMsg_NavigateToURLBlockUntilNavigationsComplete,
        NavigateToURLBlockUntilNavigationsComplete)
    IPC_MESSAGE_HANDLER(AutomationMsg_NavigationAsync, NavigationAsync)
    IPC_MESSAGE_HANDLER(AutomationMsg_NavigationAsyncWithDisposition,
                        NavigationAsyncWithDisposition)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_Reload, Reload)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_SetAuth, SetAuth)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_CancelAuth, CancelAuth)
    IPC_MESSAGE_HANDLER(AutomationMsg_NeedsAuth, NeedsAuth)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_RedirectsFrom,
                                    GetRedirectsFrom)
    IPC_MESSAGE_HANDLER(AutomationMsg_BrowserWindowCount, GetBrowserWindowCount)
    IPC_MESSAGE_HANDLER(AutomationMsg_NormalBrowserWindowCount,
                        GetNormalBrowserWindowCount)
    IPC_MESSAGE_HANDLER(AutomationMsg_BrowserWindow, GetBrowserWindow)
    IPC_MESSAGE_HANDLER(AutomationMsg_GetBrowserLocale, GetBrowserLocale)
    IPC_MESSAGE_HANDLER(AutomationMsg_LastActiveBrowserWindow,
                        GetLastActiveBrowserWindow)
    IPC_MESSAGE_HANDLER(AutomationMsg_ActiveWindow, GetActiveWindow)
    IPC_MESSAGE_HANDLER(AutomationMsg_FindNormalBrowserWindow,
                        FindNormalBrowserWindow)
    IPC_MESSAGE_HANDLER(AutomationMsg_IsWindowActive, IsWindowActive)
    IPC_MESSAGE_HANDLER(AutomationMsg_ActivateWindow, ActivateWindow)
    IPC_MESSAGE_HANDLER(AutomationMsg_IsWindowMaximized, IsWindowMaximized)
    IPC_MESSAGE_HANDLER(AutomationMsg_WindowExecuteCommandAsync,
                        ExecuteBrowserCommandAsync)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WindowExecuteCommand,
                        ExecuteBrowserCommand)
    IPC_MESSAGE_HANDLER(AutomationMsg_TerminateSession, TerminateSession)
    IPC_MESSAGE_HANDLER(AutomationMsg_WindowViewBounds, WindowGetViewBounds)
    IPC_MESSAGE_HANDLER(AutomationMsg_GetWindowBounds, GetWindowBounds)
    IPC_MESSAGE_HANDLER(AutomationMsg_SetWindowBounds, SetWindowBounds)
    IPC_MESSAGE_HANDLER(AutomationMsg_SetWindowVisible, SetWindowVisible)
    IPC_MESSAGE_HANDLER(AutomationMsg_WindowClick, WindowSimulateClick)
    IPC_MESSAGE_HANDLER(AutomationMsg_WindowMouseMove, WindowSimulateMouseMove)
    IPC_MESSAGE_HANDLER(AutomationMsg_WindowKeyPress, WindowSimulateKeyPress)
    IPC_MESSAGE_HANDLER(AutomationMsg_TabCount, GetTabCount)
    IPC_MESSAGE_HANDLER(AutomationMsg_Type, GetType)
    IPC_MESSAGE_HANDLER(AutomationMsg_Tab, GetTab)
    IPC_MESSAGE_HANDLER(AutomationMsg_TabProcessID, GetTabProcessID)
    IPC_MESSAGE_HANDLER(AutomationMsg_TabTitle, GetTabTitle)
    IPC_MESSAGE_HANDLER(AutomationMsg_TabIndex, GetTabIndex)
    IPC_MESSAGE_HANDLER(AutomationMsg_TabURL, GetTabURL)
    IPC_MESSAGE_HANDLER(AutomationMsg_ShelfVisibility, GetShelfVisibility)
    IPC_MESSAGE_HANDLER(AutomationMsg_IsFullscreen, IsFullscreen)
    IPC_MESSAGE_HANDLER(AutomationMsg_IsFullscreenBubbleVisible,
                        GetFullscreenBubbleVisibility)
    IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditForBrowser,
                        GetAutocompleteEditForBrowser)
    IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditGetText,
                        GetAutocompleteEditText)
    IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditSetText,
                        SetAutocompleteEditText)
    IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditIsQueryInProgress,
                        AutocompleteEditIsQueryInProgress)
    IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditGetMatches,
                        AutocompleteEditGetMatches)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForAutocompleteEditFocus,
                                    WaitForAutocompleteEditFocus)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_DomOperation,
                                    ExecuteJavascript)
    IPC_MESSAGE_HANDLER(AutomationMsg_ConstrainedWindowCount,
                        GetConstrainedWindowCount)
#if defined(TOOLKIT_VIEWS)
    IPC_MESSAGE_HANDLER(AutomationMsg_GetFocusedViewID, GetFocusedViewID)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForFocusedViewIDToChange,
                                    WaitForFocusedViewIDToChange)
    IPC_MESSAGE_HANDLER(AutomationMsg_StartTrackingPopupMenus,
                        StartTrackingPopupMenus)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForPopupMenuToOpen,
                                    WaitForPopupMenuToOpen)
#endif  // defined(TOOLKIT_VIEWS)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_InspectElement,
                                    HandleInspectElementRequest)
    IPC_MESSAGE_HANDLER(AutomationMsg_DownloadDirectory, GetDownloadDirectory)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_OpenNewBrowserWindowOfType,
                                    OpenNewBrowserWindowOfType)
    IPC_MESSAGE_HANDLER(AutomationMsg_WindowForBrowser, GetWindowForBrowser)
    IPC_MESSAGE_HANDLER(AutomationMsg_BrowserForWindow, GetBrowserForWindow)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_ShowInterstitialPage,
                                    ShowInterstitialPage)
    IPC_MESSAGE_HANDLER(AutomationMsg_HideInterstitialPage,
                        HideInterstitialPage)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForTabToBeRestored,
                                    WaitForTabToBeRestored)
    IPC_MESSAGE_HANDLER(AutomationMsg_GetSecurityState, GetSecurityState)
    IPC_MESSAGE_HANDLER(AutomationMsg_GetPageType, GetPageType)
    IPC_MESSAGE_HANDLER(AutomationMsg_GetMetricEventDuration,
                        GetMetricEventDuration)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_ActionOnSSLBlockingPage,
                                    ActionOnSSLBlockingPage)
    IPC_MESSAGE_HANDLER(AutomationMsg_BringBrowserToFront, BringBrowserToFront)
    IPC_MESSAGE_HANDLER(AutomationMsg_IsMenuCommandEnabled,
                        IsMenuCommandEnabled)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_PrintNow, PrintNow)
    IPC_MESSAGE_HANDLER(AutomationMsg_SavePage, SavePage)
    IPC_MESSAGE_HANDLER(AutomationMsg_OpenFindInPage,
                        HandleOpenFindInPageRequest)
    IPC_MESSAGE_HANDLER(AutomationMsg_FindWindowVisibility,
                        GetFindWindowVisibility)
    IPC_MESSAGE_HANDLER(AutomationMsg_FindWindowLocation,
                        HandleFindWindowLocationRequest)
    IPC_MESSAGE_HANDLER(AutomationMsg_BookmarkBarVisibility,
                        GetBookmarkBarVisibility)
    IPC_MESSAGE_HANDLER(AutomationMsg_GetBookmarksAsJSON,
                        GetBookmarksAsJSON)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForBookmarkModelToLoad,
                                    WaitForBookmarkModelToLoad)
    IPC_MESSAGE_HANDLER(AutomationMsg_AddBookmarkGroup,
                        AddBookmarkGroup)
    IPC_MESSAGE_HANDLER(AutomationMsg_AddBookmarkURL,
                        AddBookmarkURL)
    IPC_MESSAGE_HANDLER(AutomationMsg_ReparentBookmark,
                        ReparentBookmark)
    IPC_MESSAGE_HANDLER(AutomationMsg_SetBookmarkTitle,
                        SetBookmarkTitle)
    IPC_MESSAGE_HANDLER(AutomationMsg_SetBookmarkURL,
                        SetBookmarkURL)
    IPC_MESSAGE_HANDLER(AutomationMsg_RemoveBookmark,
                        RemoveBookmark)
    IPC_MESSAGE_HANDLER(AutomationMsg_GetInfoBarCount, GetInfoBarCount)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_ClickInfoBarAccept,
                                    ClickInfoBarAccept)
    IPC_MESSAGE_HANDLER(AutomationMsg_GetLastNavigationTime,
                        GetLastNavigationTime)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForNavigation,
                                    WaitForNavigation)
    IPC_MESSAGE_HANDLER(AutomationMsg_SetIntPreference, SetIntPreference)
    IPC_MESSAGE_HANDLER(AutomationMsg_ShowingAppModalDialog,
                        GetShowingAppModalDialog)
    IPC_MESSAGE_HANDLER(AutomationMsg_ClickAppModalDialogButton,
                        ClickAppModalDialogButton)
    IPC_MESSAGE_HANDLER(AutomationMsg_SetStringPreference, SetStringPreference)
    IPC_MESSAGE_HANDLER(AutomationMsg_GetBooleanPreference,
                        GetBooleanPreference)
    IPC_MESSAGE_HANDLER(AutomationMsg_SetBooleanPreference,
                        SetBooleanPreference)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(
        AutomationMsg_WaitForBrowserWindowCountToBecome,
        WaitForBrowserWindowCountToBecome)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(
        AutomationMsg_WaitForAppModalDialogToBeShown,
        WaitForAppModalDialogToBeShown)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(
        AutomationMsg_GoBackBlockUntilNavigationsComplete,
        GoBackBlockUntilNavigationsComplete)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(
        AutomationMsg_GoForwardBlockUntilNavigationsComplete,
        GoForwardBlockUntilNavigationsComplete)
    IPC_MESSAGE_HANDLER(AutomationMsg_SavePackageShouldPromptUser,
                        SavePackageShouldPromptUser)
    IPC_MESSAGE_HANDLER(AutomationMsg_WindowTitle, GetWindowTitle)
    IPC_MESSAGE_HANDLER(AutomationMsg_SetShelfVisibility, SetShelfVisibility)
    IPC_MESSAGE_HANDLER(AutomationMsg_BlockedPopupCount, GetBlockedPopupCount)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_SendJSONRequest,
                                    SendJSONRequest)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForTabCountToBecome,
                                    WaitForTabCountToBecome)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForInfoBarCount,
                                    WaitForInfoBarCount)
    IPC_MESSAGE_HANDLER(AutomationMsg_GetPageCurrentEncoding,
                        GetPageCurrentEncoding)
    IPC_MESSAGE_HANDLER(AutomationMsg_ShutdownSessionService,
                        ShutdownSessionService)
    IPC_MESSAGE_HANDLER(AutomationMsg_SetContentSetting, SetContentSetting)
    IPC_MESSAGE_HANDLER(AutomationMsg_LoadBlockedPlugins, LoadBlockedPlugins)
    IPC_MESSAGE_HANDLER(AutomationMsg_ResetToDefaultTheme, ResetToDefaultTheme)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(
        AutomationMsg_WaitForProcessLauncherThreadToGoIdle,
        WaitForProcessLauncherThreadToGoIdle)
    IPC_MESSAGE_HANDLER(AutomationMsg_GetParentBrowserOfTab,
                        GetParentBrowserOfTab)

    IPC_MESSAGE_UNHANDLED(
        handled = AutomationProvider::OnMessageReceived(message))
  IPC_END_MESSAGE_MAP_EX()
  if (!deserialize_success)
    OnMessageDeserializationFailure();
  return handled;
}

void TestingAutomationProvider::OnChannelError() {
  if (!reinitialize_on_channel_error_ &&
      browser_shutdown::GetShutdownType() == browser_shutdown::NOT_VALID)
    BrowserList::CloseAllBrowsersAndExit();
  AutomationProvider::OnChannelError();
}

void TestingAutomationProvider::CloseBrowser(int browser_handle,
                                             IPC::Message* reply_message) {
  if (!browser_tracker_->ContainsHandle(browser_handle))
    return;

  Browser* browser = browser_tracker_->GetResource(browser_handle);
  new BrowserClosedNotificationObserver(browser, this, reply_message);
  browser->window()->Close();
}

void TestingAutomationProvider::CloseBrowserAsync(int browser_handle) {
  if (!browser_tracker_->ContainsHandle(browser_handle))
    return;

  Browser* browser = browser_tracker_->GetResource(browser_handle);
  browser->window()->Close();
}

void TestingAutomationProvider::ActivateTab(int handle,
                                            int at_index,
                                            int* status) {
  *status = -1;
  if (browser_tracker_->ContainsHandle(handle) && at_index > -1) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (at_index >= 0 && at_index < browser->tab_count()) {
      browser->ActivateTabAt(at_index, true);
      *status = 0;
    }
  }
}

void TestingAutomationProvider::AppendTab(int handle,
                                          const GURL& url,
                                          IPC::Message* reply_message) {
  int append_tab_response = -1;  // -1 is the error code
  NotificationObserver* observer = NULL;

  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    observer = new TabAppendedNotificationObserver(browser, this,
                                                   reply_message);
    TabContentsWrapper* contents =
        browser->AddSelectedTabWithURL(url, PageTransition::TYPED);
    if (contents) {
      append_tab_response =
          GetIndexForNavigationController(&contents->controller(), browser);
    }
  }

  if (append_tab_response < 0) {
    // Appending tab failed. Clean up and send failure response.

    if (observer)
      delete observer;

    AutomationMsg_AppendTab::WriteReplyParams(reply_message,
                                              append_tab_response);
    Send(reply_message);
  }
}

void TestingAutomationProvider::GetActiveTabIndex(int handle,
                                                  int* active_tab_index) {
  *active_tab_index = -1;  // -1 is the error code
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    *active_tab_index = browser->active_index();
  }
}

void TestingAutomationProvider::CloseTab(int tab_handle,
                                         bool wait_until_closed,
                                         IPC::Message* reply_message) {
  if (tab_tracker_->ContainsHandle(tab_handle)) {
    NavigationController* controller = tab_tracker_->GetResource(tab_handle);
    int index;
    Browser* browser = Browser::GetBrowserForController(controller, &index);
    DCHECK(browser);
    new TabClosedNotificationObserver(this, wait_until_closed, reply_message);
    browser->CloseTabContents(controller->tab_contents());
    return;
  }

  AutomationMsg_CloseTab::WriteReplyParams(reply_message, false);
  Send(reply_message);
}

void TestingAutomationProvider::GetCookies(const GURL& url, int handle,
                                           int* value_size,
                                           std::string* value) {
  TabContents *contents = tab_tracker_->ContainsHandle(handle) ?
      tab_tracker_->GetResource(handle)->tab_contents() : NULL;
  automation_util::GetCookies(url, contents, value_size, value);
}

void TestingAutomationProvider::SetCookie(const GURL& url,
                                          const std::string& value,
                                          int handle,
                                          int* response_value) {
  TabContents *contents = tab_tracker_->ContainsHandle(handle) ?
      tab_tracker_->GetResource(handle)->tab_contents() : NULL;
  automation_util::SetCookie(url, value, contents, response_value);
}

void TestingAutomationProvider::DeleteCookie(const GURL& url,
                                             const std::string& cookie_name,
                                             int handle, bool* success) {
  TabContents *contents = tab_tracker_->ContainsHandle(handle) ?
      tab_tracker_->GetResource(handle)->tab_contents() : NULL;
  automation_util::DeleteCookie(url, cookie_name, contents, success);
}

void TestingAutomationProvider::ShowCollectedCookiesDialog(
    int handle, bool* success) {
  *success = false;
  if (tab_tracker_->ContainsHandle(handle)) {
    TabContents* tab_contents =
        tab_tracker_->GetResource(handle)->tab_contents();
    tab_contents->delegate()->ShowCollectedCookiesDialog(tab_contents);
    *success = true;
  }
}

void TestingAutomationProvider::NavigateToURLBlockUntilNavigationsComplete(
    int handle, const GURL& url, int number_of_navigations,
    IPC::Message* reply_message) {
  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* tab = tab_tracker_->GetResource(handle);

    // Simulate what a user would do. Activate the tab and then navigate.
    // We could allow navigating in a background tab in future.
    Browser* browser = FindAndActivateTab(tab);

    if (browser) {
      new NavigationNotificationObserver(tab, this, reply_message,
                                         number_of_navigations, false, false);

      // TODO(darin): avoid conversion to GURL.
      browser->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::TYPED);
      return;
    }
  }

  AutomationMsg_NavigateToURLBlockUntilNavigationsComplete::WriteReplyParams(
      reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
  Send(reply_message);
}

void TestingAutomationProvider::NavigationAsync(int handle,
                                                const GURL& url,
                                                bool* status) {
  NavigationAsyncWithDisposition(handle, url, CURRENT_TAB, status);
}

void TestingAutomationProvider::NavigationAsyncWithDisposition(
    int handle,
    const GURL& url,
    WindowOpenDisposition disposition,
    bool* status) {
  *status = false;

  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* tab = tab_tracker_->GetResource(handle);

    // Simulate what a user would do. Activate the tab and then navigate.
    // We could allow navigating in a background tab in future.
    Browser* browser = FindAndActivateTab(tab);

    if (browser) {
      // Don't add any listener unless a callback mechanism is desired.
      // TODO(vibhor): Do this if such a requirement arises in future.
      browser->OpenURL(url, GURL(), disposition, PageTransition::TYPED);
      *status = true;
    }
  }
}

void TestingAutomationProvider::Reload(int handle,
                                       IPC::Message* reply_message) {
  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* tab = tab_tracker_->GetResource(handle);
    Browser* browser = FindAndActivateTab(tab);
    if (browser && browser->command_updater()->IsCommandEnabled(IDC_RELOAD)) {
      new NavigationNotificationObserver(
          tab, this, reply_message, 1, false, false);
      browser->Reload(CURRENT_TAB);
      return;
    }
  }

  AutomationMsg_Reload::WriteReplyParams(
      reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
  Send(reply_message);
}

void TestingAutomationProvider::SetAuth(int tab_handle,
                                        const std::wstring& username,
                                        const std::wstring& password,
                                        IPC::Message* reply_message) {
  if (tab_tracker_->ContainsHandle(tab_handle)) {
    NavigationController* tab = tab_tracker_->GetResource(tab_handle);
    LoginHandlerMap::iterator iter = login_handler_map_.find(tab);

    if (iter != login_handler_map_.end()) {
      // If auth is needed again after this, assume login has failed.  This is
      // not strictly correct, because a navigation can require both proxy and
      // server auth, but it should be OK for now.
      LoginHandler* handler = iter->second;
      new NavigationNotificationObserver(
          tab, this, reply_message, 1, false, false);
      handler->SetAuth(WideToUTF16Hack(username), WideToUTF16Hack(password));
      return;
    }
  }

  AutomationMsg_SetAuth::WriteReplyParams(
      reply_message, AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED);
  Send(reply_message);
}

void TestingAutomationProvider::CancelAuth(int tab_handle,
                                           IPC::Message* reply_message) {
  if (tab_tracker_->ContainsHandle(tab_handle)) {
    NavigationController* tab = tab_tracker_->GetResource(tab_handle);
    LoginHandlerMap::iterator iter = login_handler_map_.find(tab);

    if (iter != login_handler_map_.end()) {
      // If auth is needed again after this, something is screwy.
      LoginHandler* handler = iter->second;
      new NavigationNotificationObserver(
          tab, this, reply_message, 1, false, false);
      handler->CancelAuth();
      return;
    }
  }

  AutomationMsg_CancelAuth::WriteReplyParams(
      reply_message, AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED);
  Send(reply_message);
}

void TestingAutomationProvider::NeedsAuth(int tab_handle, bool* needs_auth) {
  *needs_auth = false;

  if (tab_tracker_->ContainsHandle(tab_handle)) {
    NavigationController* tab = tab_tracker_->GetResource(tab_handle);
    LoginHandlerMap::iterator iter = login_handler_map_.find(tab);

    if (iter != login_handler_map_.end()) {
      // The LoginHandler will be in our map IFF the tab needs auth.
      *needs_auth = true;
    }
  }
}

void TestingAutomationProvider::GetRedirectsFrom(int tab_handle,
                                                 const GURL& source_url,
                                                 IPC::Message* reply_message) {
  if (redirect_query_) {
    LOG(ERROR) << "Can only handle one redirect query at once.";
  } else if (tab_tracker_->ContainsHandle(tab_handle)) {
    NavigationController* tab = tab_tracker_->GetResource(tab_handle);
    HistoryService* history_service =
        tab->profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);

    DCHECK(history_service) << "Tab " << tab_handle << "'s profile " <<
                               "has no history service";
    if (history_service) {
      DCHECK(!reply_message_);
      reply_message_ = reply_message;
      // Schedule a history query for redirects. The response will be sent
      // asynchronously from the callback the history system uses to notify us
      // that it's done: OnRedirectQueryComplete.
      redirect_query_ = history_service->QueryRedirectsFrom(
          source_url, &consumer_,
          NewCallback(this,
                      &TestingAutomationProvider::OnRedirectQueryComplete));
      return;  // Response will be sent when query completes.
    }
  }

  // Send failure response.
  std::vector<GURL> empty;
  AutomationMsg_RedirectsFrom::WriteReplyParams(reply_message, false, empty);
  Send(reply_message);
}

void TestingAutomationProvider::GetBrowserWindowCount(int* window_count) {
  *window_count = static_cast<int>(BrowserList::size());
}

void TestingAutomationProvider::GetNormalBrowserWindowCount(int* window_count) {
  *window_count = static_cast<int>(
      BrowserList::GetBrowserCountForType(profile_, Browser::TYPE_NORMAL));
}

void TestingAutomationProvider::GetBrowserWindow(int index, int* handle) {
  *handle = 0;
  Browser* browser = automation_util::GetBrowserAt(index);
  if (browser)
    *handle = browser_tracker_->Add(browser);
}

void TestingAutomationProvider::FindNormalBrowserWindow(int* handle) {
  *handle = 0;
  Browser* browser = BrowserList::FindBrowserWithType(profile_,
                                                      Browser::TYPE_NORMAL,
                                                      false);
  if (browser)
    *handle = browser_tracker_->Add(browser);
}

void TestingAutomationProvider::GetLastActiveBrowserWindow(int* handle) {
  *handle = 0;
  Browser* browser = BrowserList::GetLastActive();
  if (browser)
    *handle = browser_tracker_->Add(browser);
}

void TestingAutomationProvider::GetActiveWindow(int* handle) {
  *handle = 0;
  Browser* browser = BrowserList::GetLastActive();
  if (browser) {
    gfx::NativeWindow window = browser->window()->GetNativeHandle();
    *handle = window_tracker_->Add(window);
  }
}

void TestingAutomationProvider::ExecuteBrowserCommandAsync(int handle,
                                                           int command,
                                                           bool* success) {
  *success = false;
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (browser->command_updater()->SupportsCommand(command) &&
        browser->command_updater()->IsCommandEnabled(command)) {
      browser->ExecuteCommand(command);
      *success = true;
    }
  }
}

void TestingAutomationProvider::ExecuteBrowserCommand(
    int handle, int command, IPC::Message* reply_message) {
  // List of commands which just finish synchronously and don't require
  // setting up an observer.
  static const int kSynchronousCommands[] = {
    IDC_HOME,
    IDC_SELECT_NEXT_TAB,
    IDC_SELECT_PREVIOUS_TAB,
    IDC_SHOW_BOOKMARK_MANAGER,
  };
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (browser->command_updater()->SupportsCommand(command) &&
        browser->command_updater()->IsCommandEnabled(command)) {
      // First check if we can handle the command without using an observer.
      for (size_t i = 0; i < arraysize(kSynchronousCommands); i++) {
        if (command == kSynchronousCommands[i]) {
          browser->ExecuteCommand(command);
          AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message,
                                                               true);
          Send(reply_message);
          return;
        }
      }

      // Use an observer if we have one, otherwise fail.
      if (ExecuteBrowserCommandObserver::CreateAndRegisterObserver(
          this, browser, command, reply_message)) {
        browser->ExecuteCommand(command);
        return;
      }
    }
  }
  AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message, false);
  Send(reply_message);
}

void TestingAutomationProvider::GetBrowserLocale(string16* locale) {
  *locale = ASCIIToUTF16(g_browser_process->GetApplicationLocale());
}

void TestingAutomationProvider::IsWindowActive(int handle,
                                               bool* success,
                                               bool* is_active) {
  if (window_tracker_->ContainsHandle(handle)) {
    *is_active =
        platform_util::IsWindowActive(window_tracker_->GetResource(handle));
    *success = true;
  } else {
    *success = false;
    *is_active = false;
  }
}

void TestingAutomationProvider::WindowSimulateClick(const IPC::Message& message,
                                                    int handle,
                                                    const gfx::Point& click,
                                                    int flags) {
  if (window_tracker_->ContainsHandle(handle)) {
    // TODO(phajdan.jr): This is flaky. We should wait for the final click.
    ui_controls::SendMouseMoveNotifyWhenDone(
        click.x(), click.y(), NewRunnableFunction(&SendMouseClick, flags));
  }
}

void TestingAutomationProvider::WindowSimulateMouseMove(
    const IPC::Message& message,
    int handle,
    const gfx::Point& location) {
  if (window_tracker_->ContainsHandle(handle))
    ui_controls::SendMouseMove(location.x(), location.y());
}

void TestingAutomationProvider::WindowSimulateKeyPress(
    const IPC::Message& message,
    int handle,
    int key,
    int flags) {
  if (!window_tracker_->ContainsHandle(handle))
    return;

  gfx::NativeWindow window = window_tracker_->GetResource(handle);
  // The key event is sent to whatever window is active.
  ui_controls::SendKeyPress(window, static_cast<ui::KeyboardCode>(key),
                            ((flags & ui::EF_CONTROL_DOWN) ==
                             ui::EF_CONTROL_DOWN),
                            ((flags & ui::EF_SHIFT_DOWN) ==
                             ui::EF_SHIFT_DOWN),
                            ((flags & ui::EF_ALT_DOWN) ==
                             ui::EF_ALT_DOWN),
                            ((flags & ui::EF_COMMAND_DOWN) ==
                             ui::EF_COMMAND_DOWN));
}

void TestingAutomationProvider::WebkitMouseClick(DictionaryValue* args,
                                                 IPC::Message* reply_message) {
  TabContents* tab_contents;
  std::string error;
  if (!GetTabFromJSONArgs(args, &tab_contents, &error)) {
    AutomationJSONReply(this, reply_message).SendError(error);
    return;
  }

  WebKit::WebMouseEvent mouse_event;
  if (!args->GetInteger("x", &mouse_event.x) ||
      !args->GetInteger("y", &mouse_event.y)) {
    AutomationJSONReply(this, reply_message)
        .SendError("(X,Y) coordinates missing or invalid");
    return;
  }

  int button;
  if (!args->GetInteger("button", &button)) {
    AutomationJSONReply(this, reply_message)
        .SendError("Mouse button missing or invalid");
    return;
  }
  if (button == automation::kLeftButton) {
    mouse_event.button = WebKit::WebMouseEvent::ButtonLeft;
  } else if (button == automation::kRightButton) {
    mouse_event.button = WebKit::WebMouseEvent::ButtonRight;
  } else if (button == automation::kMiddleButton) {
    mouse_event.button = WebKit::WebMouseEvent::ButtonMiddle;
  } else {
    AutomationJSONReply(this, reply_message)
        .SendError("Invalid button press requested");
    return;
  }

  mouse_event.type = WebKit::WebInputEvent::MouseDown;
  mouse_event.clickCount = 1;

  tab_contents->render_view_host()->ForwardMouseEvent(mouse_event);

  mouse_event.type = WebKit::WebInputEvent::MouseUp;
  new InputEventAckNotificationObserver(this, reply_message, mouse_event.type);
  tab_contents->render_view_host()->ForwardMouseEvent(mouse_event);
}

void TestingAutomationProvider::WebkitMouseMove(
    DictionaryValue* args, IPC::Message* reply_message) {
  TabContents* tab_contents;
  std::string error;
  if (!GetTabFromJSONArgs(args, &tab_contents, &error)) {
    AutomationJSONReply(this, reply_message).SendError(error);
    return;
  }

  WebKit::WebMouseEvent mouse_event;
  if (!args->GetInteger("x", &mouse_event.x) ||
      !args->GetInteger("y", &mouse_event.y)) {
    AutomationJSONReply(this, reply_message)
        .SendError("(X,Y) coordinates missing or invalid");
    return;
  }

  mouse_event.type = WebKit::WebInputEvent::MouseMove;
  new InputEventAckNotificationObserver(this, reply_message, mouse_event.type);
  tab_contents->render_view_host()->ForwardMouseEvent(mouse_event);
}

void TestingAutomationProvider::WebkitMouseDrag(DictionaryValue* args,
                                                IPC::Message* reply_message) {
  TabContents* tab_contents;
  std::string error;
  if (!GetTabFromJSONArgs(args, &tab_contents, &error)) {
    AutomationJSONReply(this, reply_message).SendError(error);
    return;
  }

  WebKit::WebMouseEvent mouse_event;
  int start_x, start_y, end_x, end_y;
  if (!args->GetInteger("start_x", &start_x) ||
      !args->GetInteger("start_y", &start_y) ||
      !args->GetInteger("end_x", &end_x) ||
      !args->GetInteger("end_y", &end_y)) {
    AutomationJSONReply(this, reply_message)
        .SendError("Invalid start/end positions");
    return;
  }

  mouse_event.type = WebKit::WebInputEvent::MouseMove;
  // Step 1- Move the mouse to the start position.
  mouse_event.x = start_x;
  mouse_event.y = start_y;
  tab_contents->render_view_host()->ForwardMouseEvent(mouse_event);

  // Step 2- Left click mouse down, the mouse button is fixed.
  mouse_event.type = WebKit::WebInputEvent::MouseDown;
  mouse_event.button = WebKit::WebMouseEvent::ButtonLeft;
  mouse_event.clickCount = 1;
  tab_contents->render_view_host()->ForwardMouseEvent(mouse_event);

  // Step 3 - Move the mouse to the end position.
  // TODO(JMikhail): See if we should simulate the by not making such
  // a drastic jump by placing incrmental stops along the way.
  mouse_event.type = WebKit::WebInputEvent::MouseMove;
  mouse_event.x = end_x;
  mouse_event.y = end_y;
  mouse_event.clickCount = 0;
  tab_contents->render_view_host()->ForwardMouseEvent(mouse_event);

  // Step 4 - Release the left mouse button.
  mouse_event.type = WebKit::WebInputEvent::MouseUp;
  mouse_event.clickCount = 1;
  new InputEventAckNotificationObserver(this, reply_message, mouse_event.type);
  tab_contents->render_view_host()->ForwardMouseEvent(mouse_event);
}

void TestingAutomationProvider::GetTabCount(int handle, int* tab_count) {
  *tab_count = -1;  // -1 is the error code

  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    *tab_count = browser->tab_count();
  }
}

void TestingAutomationProvider::GetType(int handle, int* type_as_int) {
  *type_as_int = -1;  // -1 is the error code

  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    *type_as_int = static_cast<int>(browser->type());
  }
}

void TestingAutomationProvider::GetTab(int win_handle,
                                       int tab_index,
                                       int* tab_handle) {
  *tab_handle = 0;
  if (browser_tracker_->ContainsHandle(win_handle) && (tab_index >= 0)) {
    Browser* browser = browser_tracker_->GetResource(win_handle);
    if (tab_index < browser->tab_count()) {
      TabContents* tab_contents = browser->GetTabContentsAt(tab_index);
      *tab_handle = tab_tracker_->Add(&tab_contents->controller());
    }
  }
}

void TestingAutomationProvider::GetTabProcessID(int handle, int* process_id) {
  *process_id = -1;

  if (tab_tracker_->ContainsHandle(handle)) {
    *process_id = 0;
    TabContents* tab_contents =
        tab_tracker_->GetResource(handle)->tab_contents();
    RenderProcessHost* rph = tab_contents->GetRenderProcessHost();
    if (rph)
      *process_id = base::GetProcId(rph->GetHandle());
  }
}

void TestingAutomationProvider::GetTabTitle(int handle,
                                            int* title_string_size,
                                            std::wstring* title) {
  *title_string_size = -1;  // -1 is the error code
  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* tab = tab_tracker_->GetResource(handle);
    NavigationEntry* entry = tab->GetActiveEntry();
    if (entry != NULL) {
      *title = UTF16ToWideHack(entry->GetTitleForDisplay(""));
    } else {
      *title = std::wstring();
    }
    *title_string_size = static_cast<int>(title->size());
  }
}

void TestingAutomationProvider::GetTabIndex(int handle, int* tabstrip_index) {
  *tabstrip_index = -1;  // -1 is the error code

  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* tab = tab_tracker_->GetResource(handle);
    Browser* browser = Browser::GetBrowserForController(tab, NULL);
    *tabstrip_index = browser->tabstrip_model()->GetIndexOfController(tab);
  }
}

void TestingAutomationProvider::GetTabURL(int handle,
                                          bool* success,
                                          GURL* url) {
  *success = false;
  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* tab = tab_tracker_->GetResource(handle);
    // Return what the user would see in the location bar.
    *url = tab->GetActiveEntry()->virtual_url();
    *success = true;
  }
}

void TestingAutomationProvider::GetShelfVisibility(int handle, bool* visible) {
  *visible = false;

  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (browser) {
#if defined(OS_CHROMEOS)
      *visible = ActiveDownloadsUI::GetPopup(browser->profile());
#else
      *visible = browser->window()->IsDownloadShelfVisible();
#endif
    }
  }
}

void TestingAutomationProvider::IsFullscreen(int handle, bool* visible) {
  *visible = false;

  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (browser)
      *visible = browser->window()->IsFullscreen();
  }
}

void TestingAutomationProvider::GetFullscreenBubbleVisibility(int handle,
                                                              bool* visible) {
  *visible = false;

  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (browser)
      *visible = browser->window()->IsFullscreenBubbleVisible();
  }
}

void TestingAutomationProvider::GetAutocompleteEditText(
    int autocomplete_edit_handle,
    bool* success,
    string16* text) {
  *success = false;
  if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) {
    *text = autocomplete_edit_tracker_->GetResource(autocomplete_edit_handle)->
        GetText();
    *success = true;
  }
}

void TestingAutomationProvider::SetAutocompleteEditText(
    int autocomplete_edit_handle,
    const string16& text,
    bool* success) {
  *success = false;
  if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) {
    autocomplete_edit_tracker_->GetResource(autocomplete_edit_handle)->
        SetUserText(text);
    *success = true;
  }
}

void TestingAutomationProvider::AutocompleteEditGetMatches(
    int autocomplete_edit_handle,
    bool* success,
    std::vector<AutocompleteMatchData>* matches) {
  *success = false;
  if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) {
    const AutocompleteResult& result = autocomplete_edit_tracker_->
        GetResource(autocomplete_edit_handle)->model()->result();
    for (AutocompleteResult::const_iterator i = result.begin();
        i != result.end(); ++i)
      matches->push_back(AutocompleteMatchData(*i));
    *success = true;
  }
}

// Waits for the autocomplete edit to receive focus
void  TestingAutomationProvider::WaitForAutocompleteEditFocus(
    int autocomplete_edit_handle,
    IPC::Message* reply_message) {
  if (!autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) {
    AutomationMsg_WaitForAutocompleteEditFocus::WriteReplyParams(
        reply_message_, false);
    Send(reply_message);
    return;
  }

  AutocompleteEditModel* model = autocomplete_edit_tracker_->
      GetResource(autocomplete_edit_handle)-> model();
  if (model->has_focus()) {
    AutomationMsg_WaitForAutocompleteEditFocus::WriteReplyParams(
        reply_message, true);
    Send(reply_message);
    return;
  }

  // The observer deletes itself when the notification arrives.
  new AutocompleteEditFocusedObserver(this, model, reply_message);
}

void TestingAutomationProvider::GetAutocompleteEditForBrowser(
    int browser_handle,
    bool* success,
    int* autocomplete_edit_handle) {
  *success = false;
  *autocomplete_edit_handle = 0;

  if (browser_tracker_->ContainsHandle(browser_handle)) {
    Browser* browser = browser_tracker_->GetResource(browser_handle);
    LocationBar* loc_bar = browser->window()->GetLocationBar();
    AutocompleteEditView* edit_view = loc_bar->location_entry();
    // Add() returns the existing handle for the resource if any.
    *autocomplete_edit_handle = autocomplete_edit_tracker_->Add(edit_view);
    *success = true;
  }
}

void TestingAutomationProvider::AutocompleteEditIsQueryInProgress(
    int autocomplete_edit_handle,
    bool* success,
    bool* query_in_progress) {
  *success = false;
  *query_in_progress = false;
  if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) {
    *query_in_progress = !autocomplete_edit_tracker_->
        GetResource(autocomplete_edit_handle)->model()->
        autocomplete_controller()->done();
    *success = true;
  }
}

void TestingAutomationProvider::ExecuteJavascript(
    int handle,
    const std::wstring& frame_xpath,
    const std::wstring& script,
    IPC::Message* reply_message) {
  TabContents* tab_contents = GetTabContentsForHandle(handle, NULL);
  if (!tab_contents) {
    AutomationMsg_DomOperation::WriteReplyParams(reply_message, std::string());
    Send(reply_message);
    return;
  }

  // Set the routing id of this message with the controller.
  // This routing id needs to be remembered for the reverse
  // communication while sending back the response of
  // this javascript execution.
  std::string set_automation_id;
  base::SStringPrintf(&set_automation_id,
                      "window.domAutomationController.setAutomationId(%d);",
                      reply_message->routing_id());

  new DomOperationMessageSender(this, reply_message, false);
  tab_contents->render_view_host()->ExecuteJavascriptInWebFrame(
      WideToUTF16Hack(frame_xpath), UTF8ToUTF16(set_automation_id));
  tab_contents->render_view_host()->ExecuteJavascriptInWebFrame(
      WideToUTF16Hack(frame_xpath), WideToUTF16Hack(script));
}

void TestingAutomationProvider::GetConstrainedWindowCount(int handle,
                                                          int* count) {
  *count = -1;  // -1 is the error code
  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* nav_controller = tab_tracker_->GetResource(handle);
    TabContents* tab_contents = nav_controller->tab_contents();
    if (tab_contents)
      *count = static_cast<int>(tab_contents->child_windows_.size());
  }
}

void TestingAutomationProvider::HandleInspectElementRequest(
    int handle, int x, int y, IPC::Message* reply_message) {
  TabContents* tab_contents = GetTabContentsForHandle(handle, NULL);
  if (tab_contents) {
    DCHECK(!reply_message_);
    reply_message_ = reply_message;

    DevToolsManager::GetInstance()->InspectElement(
        tab_contents->render_view_host(), x, y);
  } else {
    AutomationMsg_InspectElement::WriteReplyParams(reply_message, -1);
    Send(reply_message);
  }
}

void TestingAutomationProvider::GetDownloadDirectory(
    int handle, FilePath* download_directory) {
  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* tab = tab_tracker_->GetResource(handle);
    DownloadManager* dlm = tab->profile()->GetDownloadManager();
    *download_directory = dlm->download_prefs()->download_path();
  }
}

void TestingAutomationProvider::OpenNewBrowserWindowOfType(
    int type, bool show, IPC::Message* reply_message) {
  new BrowserOpenedNotificationObserver(this, reply_message);
  // We may have no current browser windows open so don't rely on
  // asking an existing browser to execute the IDC_NEWWINDOW command
  Browser* browser = new Browser(static_cast<Browser::Type>(type), profile_);
  browser->InitBrowserWindow();
  browser->AddBlankTab(true);
  if (show)
    browser->window()->Show();
}

void TestingAutomationProvider::GetWindowForBrowser(int browser_handle,
                                                    bool* success,
                                                    int* handle) {
  *success = false;
  *handle = 0;

  if (browser_tracker_->ContainsHandle(browser_handle)) {
    Browser* browser = browser_tracker_->GetResource(browser_handle);
    gfx::NativeWindow win = browser->window()->GetNativeHandle();
    // Add() returns the existing handle for the resource if any.
    *handle = window_tracker_->Add(win);
    *success = true;
  }
}

void TestingAutomationProvider::GetBrowserForWindow(int window_handle,
                                                    bool* success,
                                                    int* browser_handle) {
  *success = false;
  *browser_handle = 0;

  gfx::NativeWindow window = window_tracker_->GetResource(window_handle);
  if (!window)
    return;

  BrowserList::const_iterator iter = BrowserList::begin();
  for (;iter != BrowserList::end(); ++iter) {
    gfx::NativeWindow this_window = (*iter)->window()->GetNativeHandle();
    if (window == this_window) {
      // Add() returns the existing handle for the resource if any.
      *browser_handle = browser_tracker_->Add(*iter);
      *success = true;
      return;
    }
  }
}

void TestingAutomationProvider::ShowInterstitialPage(
    int tab_handle,
    const std::string& html_text,
    IPC::Message* reply_message) {
  if (tab_tracker_->ContainsHandle(tab_handle)) {
    NavigationController* controller = tab_tracker_->GetResource(tab_handle);
    TabContents* tab_contents = controller->tab_contents();

    new NavigationNotificationObserver(controller, this, reply_message, 1,
                                       false, false);

    AutomationInterstitialPage* interstitial =
        new AutomationInterstitialPage(tab_contents,
                                       GURL("about:interstitial"),
                                       html_text);
    interstitial->Show();
    return;
  }

  AutomationMsg_ShowInterstitialPage::WriteReplyParams(
      reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
  Send(reply_message);
}

void TestingAutomationProvider::HideInterstitialPage(int tab_handle,
                                                     bool* success) {
  *success = false;
  TabContents* tab_contents = GetTabContentsForHandle(tab_handle, NULL);
  if (tab_contents && tab_contents->interstitial_page()) {
    tab_contents->interstitial_page()->DontProceed();
    *success = true;
  }
}

void TestingAutomationProvider::WaitForTabToBeRestored(
    int tab_handle,
    IPC::Message* reply_message) {
  if (tab_tracker_->ContainsHandle(tab_handle)) {
    NavigationController* tab = tab_tracker_->GetResource(tab_handle);
    restore_tracker_.reset(
        new NavigationControllerRestoredObserver(this, tab, reply_message));
  } else {
    AutomationMsg_WaitForTabToBeRestored::WriteReplyParams(
        reply_message, false);
    Send(reply_message);
  }
}

void TestingAutomationProvider::GetSecurityState(int handle,
                                                 bool* success,
                                                 SecurityStyle* security_style,
                                                 int* ssl_cert_status,
                                                 int* insecure_content_status) {
  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* tab = tab_tracker_->GetResource(handle);
    NavigationEntry* entry = tab->GetActiveEntry();
    *success = true;
    *security_style = entry->ssl().security_style();
    *ssl_cert_status = entry->ssl().cert_status();
    *insecure_content_status = entry->ssl().content_status();
  } else {
    *success = false;
    *security_style = SECURITY_STYLE_UNKNOWN;
    *ssl_cert_status = 0;
    *insecure_content_status = 0;
  }
}

void TestingAutomationProvider::GetPageType(
    int handle,
    bool* success,
    PageType* page_type) {
  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* tab = tab_tracker_->GetResource(handle);
    NavigationEntry* entry = tab->GetActiveEntry();
    *page_type = entry->page_type();
    *success = true;
    // In order to return the proper result when an interstitial is shown and
    // no navigation entry were created for it we need to ask the TabContents.
    if (*page_type == NORMAL_PAGE &&
        tab->tab_contents()->showing_interstitial_page())
      *page_type = INTERSTITIAL_PAGE;
  } else {
    *success = false;
    *page_type = NORMAL_PAGE;
  }
}

void TestingAutomationProvider::GetMetricEventDuration(
    const std::string& event_name,
    int* duration_ms) {
  *duration_ms = metric_event_duration_observer_->GetEventDurationMs(
      event_name);
}

void TestingAutomationProvider::ActionOnSSLBlockingPage(
    int handle,
    bool proceed,
    IPC::Message* reply_message) {
  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* tab = tab_tracker_->GetResource(handle);
    NavigationEntry* entry = tab->GetActiveEntry();
    if (entry->page_type() == INTERSTITIAL_PAGE) {
      TabContents* tab_contents = tab->tab_contents();
      InterstitialPage* ssl_blocking_page =
          InterstitialPage::GetInterstitialPage(tab_contents);
      if (ssl_blocking_page) {
        if (proceed) {
          new NavigationNotificationObserver(tab, this, reply_message, 1,
                                             false, false);
          ssl_blocking_page->Proceed();
          return;
        }
        ssl_blocking_page->DontProceed();
        AutomationMsg_ActionOnSSLBlockingPage::WriteReplyParams(
            reply_message, AUTOMATION_MSG_NAVIGATION_SUCCESS);
        Send(reply_message);
        return;
      }
    }
  }
  // We failed.
  AutomationMsg_ActionOnSSLBlockingPage::WriteReplyParams(
      reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
  Send(reply_message);
}

void TestingAutomationProvider::BringBrowserToFront(int browser_handle,
                                                    bool* success) {
  *success = false;
  if (browser_tracker_->ContainsHandle(browser_handle)) {
    Browser* browser = browser_tracker_->GetResource(browser_handle);
    browser->window()->Activate();
    *success = true;
  }
}

void TestingAutomationProvider::IsMenuCommandEnabled(int browser_handle,
                                                     int message_num,
                                                     bool* menu_item_enabled) {
  *menu_item_enabled = false;
  if (browser_tracker_->ContainsHandle(browser_handle)) {
    Browser* browser = browser_tracker_->GetResource(browser_handle);
    *menu_item_enabled =
        browser->command_updater()->IsCommandEnabled(message_num);
  }
}

void TestingAutomationProvider::PrintNow(int tab_handle,
                                         IPC::Message* reply_message) {
  NavigationController* tab = NULL;
  TabContents* tab_contents = GetTabContentsForHandle(tab_handle, &tab);
  if (tab_contents) {
    FindAndActivateTab(tab);

    NotificationObserver* observer =
        new DocumentPrintedNotificationObserver(this, reply_message);

    TabContentsWrapper* wrapper =
        TabContentsWrapper::GetCurrentWrapperForContents(tab_contents);
    if (!wrapper->print_view_manager()->PrintNow()) {
      // Clean up the observer. It will send the reply message.
      delete observer;
    }

    // Return now to avoid sending reply message twice.
    return;
  }

  AutomationMsg_PrintNow::WriteReplyParams(reply_message, false);
  Send(reply_message);
}

void TestingAutomationProvider::SavePage(int tab_handle,
                                         const FilePath& file_name,
                                         const FilePath& dir_path,
                                         int type,
                                         bool* success) {
  SavePackage::SavePackageType save_type =
      static_cast<SavePackage::SavePackageType>(type);
  if (save_type < SavePackage::SAVE_AS_ONLY_HTML ||
      save_type > SavePackage::SAVE_AS_COMPLETE_HTML) {
    *success = false;
    return;
  }

  if (!tab_tracker_->ContainsHandle(tab_handle)) {
    *success = false;
    return;
  }

  NavigationController* nav = tab_tracker_->GetResource(tab_handle);
  Browser* browser = FindAndActivateTab(nav);
  if (!browser->command_updater()->IsCommandEnabled(IDC_SAVE_PAGE)) {
    *success = false;
    return;
  }

  TabContentsWrapper* wrapper =
      TabContentsWrapper::GetCurrentWrapperForContents(nav->tab_contents());
  wrapper->download_tab_helper()->SavePage(file_name, dir_path, save_type);
  *success = true;
}

void TestingAutomationProvider::HandleOpenFindInPageRequest(
    const IPC::Message& message, int handle) {
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    browser->FindInPage(false, false);
  }
}

void TestingAutomationProvider::GetFindWindowVisibility(int handle,
                                                        bool* visible) {
  *visible = false;
  Browser* browser = browser_tracker_->GetResource(handle);
  if (browser) {
    FindBarTesting* find_bar =
        browser->GetFindBarController()->find_bar()->GetFindBarTesting();
    find_bar->GetFindBarWindowInfo(NULL, visible);
  }
}

void TestingAutomationProvider::HandleFindWindowLocationRequest(int handle,
                                                                int* x,
                                                                int* y) {
  gfx::Point position(0, 0);
  bool visible = false;
  if (browser_tracker_->ContainsHandle(handle)) {
     Browser* browser = browser_tracker_->GetResource(handle);
     FindBarTesting* find_bar =
       browser->GetFindBarController()->find_bar()->GetFindBarTesting();
     find_bar->GetFindBarWindowInfo(&position, &visible);
  }

  *x = position.x();
  *y = position.y();
}

// Bookmark bar visibility is based on the pref (e.g. is it in the toolbar).
// Presence in the NTP is NOT considered visible by this call.
void TestingAutomationProvider::GetBookmarkBarVisibility(int handle,
                                                         bool* visible,
                                                         bool* animating) {
  *visible = false;
  *animating = false;

  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (browser) {
#if 0  // defined(TOOLKIT_VIEWS) && defined(OS_LINUX)
      // TODO(jrg): Was removed in rev43789 for perf. Need to investigate.

      // IsBookmarkBarVisible() line looks correct but is not
      // consistent across platforms.  Specifically, on Mac/Linux, it
      // returns false if the bar is hidden in a pref (even if visible
      // on the NTP).  On ChromeOS, it returned true if on NTP
      // independent of the pref.  Making the code more consistent
      // caused a perf bot regression on Windows (which shares views).
      // See http://crbug.com/40225
      *visible = browser->profile()->GetPrefs()->GetBoolean(
          prefs::kShowBookmarkBar);
#else
      *visible = browser->window()->IsBookmarkBarVisible();
#endif
      *animating = browser->window()->IsBookmarkBarAnimating();
    }
  }
}

void TestingAutomationProvider::GetBookmarksAsJSON(
    int handle,
    std::string* bookmarks_as_json,
    bool *success) {
  *success = false;
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (browser) {
      if (!browser->profile()->GetBookmarkModel()->IsLoaded()) {
        return;
      }
      scoped_refptr<BookmarkStorage> storage(new BookmarkStorage(
          browser->profile(),
          browser->profile()->GetBookmarkModel()));
      *success = storage->SerializeData(bookmarks_as_json);
    }
  }
}

void TestingAutomationProvider::WaitForBookmarkModelToLoad(
    int handle,
    IPC::Message* reply_message) {
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    BookmarkModel* model = browser->profile()->GetBookmarkModel();
    if (model->IsLoaded()) {
      AutomationMsg_WaitForBookmarkModelToLoad::WriteReplyParams(
          reply_message, true);
      Send(reply_message);
    } else {
      // The observer will delete itself when done.
      new AutomationProviderBookmarkModelObserver(this, reply_message,
                                                  model);
    }
  }
}

void TestingAutomationProvider::AddBookmarkGroup(int handle,
                                                 int64 parent_id,
                                                 int index,
                                                 std::wstring title,
                                                 bool* success) {
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (browser) {
      BookmarkModel* model = browser->profile()->GetBookmarkModel();
      if (!model->IsLoaded()) {
        *success = false;
        return;
      }
      const BookmarkNode* parent = model->GetNodeByID(parent_id);
      DCHECK(parent);
      if (parent) {
        const BookmarkNode* child = model->AddFolder(parent, index,
                                                     WideToUTF16Hack(title));
        DCHECK(child);
        if (child)
          *success = true;
      }
    }
  }
  *success = false;
}

void TestingAutomationProvider::AddBookmarkURL(int handle,
                                               int64 parent_id,
                                               int index,
                                               std::wstring title,
                                               const GURL& url,
                                               bool* success) {
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (browser) {
      BookmarkModel* model = browser->profile()->GetBookmarkModel();
      if (!model->IsLoaded()) {
        *success = false;
        return;
      }
      const BookmarkNode* parent = model->GetNodeByID(parent_id);
      DCHECK(parent);
      if (parent) {
        const BookmarkNode* child = model->AddURL(parent, index,
                                                  WideToUTF16Hack(title), url);
        DCHECK(child);
        if (child)
          *success = true;
      }
    }
  }
  *success = false;
}

void TestingAutomationProvider::ReparentBookmark(int handle,
                                                 int64 id,
                                                 int64 new_parent_id,
                                                 int index,
                                                 bool* success) {
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (browser) {
      BookmarkModel* model = browser->profile()->GetBookmarkModel();
      if (!model->IsLoaded()) {
        *success = false;
        return;
      }
      const BookmarkNode* node = model->GetNodeByID(id);
      DCHECK(node);
      const BookmarkNode* new_parent = model->GetNodeByID(new_parent_id);
      DCHECK(new_parent);
      if (node && new_parent) {
        model->Move(node, new_parent, index);
        *success = true;
      }
    }
  }
  *success = false;
}

void TestingAutomationProvider::SetBookmarkTitle(int handle,
                                                 int64 id,
                                                 std::wstring title,
                                                 bool* success) {
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (browser) {
      BookmarkModel* model = browser->profile()->GetBookmarkModel();
      if (!model->IsLoaded()) {
        *success = false;
        return;
      }
      const BookmarkNode* node = model->GetNodeByID(id);
      DCHECK(node);
      if (node) {
        model->SetTitle(node, WideToUTF16Hack(title));
        *success = true;
      }
    }
  }
  *success = false;
}

void TestingAutomationProvider::SetBookmarkURL(int handle,
                                               int64 id,
                                               const GURL& url,
                                               bool* success) {
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (browser) {
      BookmarkModel* model = browser->profile()->GetBookmarkModel();
      if (!model->IsLoaded()) {
        *success = false;
        return;
      }
      const BookmarkNode* node = model->GetNodeByID(id);
      DCHECK(node);
      if (node) {
        model->SetURL(node, url);
        *success = true;
      }
    }
  }
  *success = false;
}

void TestingAutomationProvider::RemoveBookmark(int handle,
                                               int64 id,
                                               bool* success) {
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (browser) {
      BookmarkModel* model = browser->profile()->GetBookmarkModel();
      if (!model->IsLoaded()) {
        *success = false;
        return;
      }
      const BookmarkNode* node = model->GetNodeByID(id);
      DCHECK(node);
      if (node) {
        const BookmarkNode* parent = node->parent();
        DCHECK(parent);
        model->Remove(parent, parent->GetIndexOf(node));
        *success = true;
      }
    }
  }
  *success = false;
}

void TestingAutomationProvider::GetInfoBarCount(int handle, size_t* count) {
  *count = static_cast<size_t>(-1);  // -1 means error.
  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* nav_controller = tab_tracker_->GetResource(handle);
    if (nav_controller)
      *count = nav_controller->tab_contents()->infobar_count();
  }
}

void TestingAutomationProvider::ClickInfoBarAccept(
    int handle,
    size_t info_bar_index,
    bool wait_for_navigation,
    IPC::Message* reply_message) {
  bool success = false;
  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* nav_controller = tab_tracker_->GetResource(handle);
    if (nav_controller) {
      if (info_bar_index < nav_controller->tab_contents()->infobar_count()) {
        if (wait_for_navigation) {
          new NavigationNotificationObserver(nav_controller, this,
                                             reply_message, 1, false, false);
        }
        InfoBarDelegate* delegate =
            nav_controller->tab_contents()->GetInfoBarDelegateAt(
                info_bar_index);
        if (delegate->AsConfirmInfoBarDelegate())
          delegate->AsConfirmInfoBarDelegate()->Accept();
        success = true;
      }
    }
  }

  // This "!wait_for_navigation || !success condition" logic looks suspicious.
  // It will send a failure message when success is true but
  // |wait_for_navigation| is false.
  // TODO(phajdan.jr): investgate whether the reply param (currently
  // AUTOMATION_MSG_NAVIGATION_ERROR) should depend on success.
  if (!wait_for_navigation || !success)
    AutomationMsg_ClickInfoBarAccept::WriteReplyParams(
        reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
}

void TestingAutomationProvider::GetLastNavigationTime(
    int handle,
    int64* last_navigation_time) {
  base::Time time(tab_tracker_->GetLastNavigationTime(handle));
  *last_navigation_time = time.ToInternalValue();
}

void TestingAutomationProvider::WaitForNavigation(int handle,
                                                  int64 last_navigation_time,
                                                  IPC::Message* reply_message) {
  NavigationController* controller = tab_tracker_->GetResource(handle);
  base::Time time(tab_tracker_->GetLastNavigationTime(handle));

  if (time.ToInternalValue() > last_navigation_time || !controller) {
    AutomationMsg_WaitForNavigation::WriteReplyParams(reply_message,
        controller == NULL ? AUTOMATION_MSG_NAVIGATION_ERROR :
                             AUTOMATION_MSG_NAVIGATION_SUCCESS);
    Send(reply_message);
    return;
  }

  new NavigationNotificationObserver(
      controller, this, reply_message, 1, true, false);
}

void TestingAutomationProvider::SetIntPreference(int handle,
                                                 const std::string& name,
                                                 int value,
                                                 bool* success) {
  *success = false;
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    browser->profile()->GetPrefs()->SetInteger(name.c_str(), value);
    *success = true;
  }
}

void TestingAutomationProvider::SetStringPreference(int handle,
                                                    const std::string& name,
                                                    const std::string& value,
                                                    bool* success) {
  *success = false;
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    browser->profile()->GetPrefs()->SetString(name.c_str(), value);
    *success = true;
  }
}

void TestingAutomationProvider::GetBooleanPreference(int handle,
                                                     const std::string& name,
                                                     bool* success,
                                                     bool* value) {
  *success = false;
  *value = false;
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    *value = browser->profile()->GetPrefs()->GetBoolean(name.c_str());
    *success = true;
  }
}

void TestingAutomationProvider::SetBooleanPreference(int handle,
                                                     const std::string& name,
                                                     bool value,
                                                     bool* success) {
  *success = false;
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    browser->profile()->GetPrefs()->SetBoolean(name.c_str(), value);
    *success = true;
  }
}

void TestingAutomationProvider::GetShowingAppModalDialog(bool* showing_dialog,
                                                         int* dialog_button) {
  AppModalDialog* active_dialog =
      AppModalDialogQueue::GetInstance()->active_dialog();
  if (!active_dialog) {
    *showing_dialog = false;
    *dialog_button = ui::MessageBoxFlags::DIALOGBUTTON_NONE;
    return;
  }
  NativeAppModalDialog* native_dialog = active_dialog->native_dialog();
  *showing_dialog = (native_dialog != NULL);
  if (*showing_dialog)
    *dialog_button = native_dialog->GetAppModalDialogButtons();
  else
    *dialog_button = ui::MessageBoxFlags::DIALOGBUTTON_NONE;
}

void TestingAutomationProvider::ClickAppModalDialogButton(int button,
                                                          bool* success) {
  *success = false;

  NativeAppModalDialog* native_dialog =
      AppModalDialogQueue::GetInstance()->active_dialog()->native_dialog();
  if (native_dialog &&
      (native_dialog->GetAppModalDialogButtons() & button) == button) {
    if ((button & ui::MessageBoxFlags::DIALOGBUTTON_OK) ==
        ui::MessageBoxFlags::DIALOGBUTTON_OK) {
      native_dialog->AcceptAppModalDialog();
      *success =  true;
    }
    if ((button & ui::MessageBoxFlags::DIALOGBUTTON_CANCEL) ==
        ui::MessageBoxFlags::DIALOGBUTTON_CANCEL) {
      DCHECK(!*success) << "invalid param, OK and CANCEL specified";
      native_dialog->CancelAppModalDialog();
      *success =  true;
    }
  }
}

void TestingAutomationProvider::WaitForBrowserWindowCountToBecome(
    int target_count,
    IPC::Message* reply_message) {
  if (static_cast<int>(BrowserList::size()) == target_count) {
    AutomationMsg_WaitForBrowserWindowCountToBecome::WriteReplyParams(
        reply_message, true);
    Send(reply_message);
    return;
  }

  // Set up an observer (it will delete itself).
  new BrowserCountChangeNotificationObserver(target_count, this, reply_message);
}

void TestingAutomationProvider::WaitForAppModalDialogToBeShown(
    IPC::Message* reply_message) {
  if (AppModalDialogQueue::GetInstance()->HasActiveDialog()) {
    AutomationMsg_WaitForAppModalDialogToBeShown::WriteReplyParams(
        reply_message, true);
    Send(reply_message);
    return;
  }

  // Set up an observer (it will delete itself).
  new AppModalDialogShownObserver(this, reply_message);
}

void TestingAutomationProvider::GoBackBlockUntilNavigationsComplete(
    int handle, int number_of_navigations, IPC::Message* reply_message) {
  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* tab = tab_tracker_->GetResource(handle);
    Browser* browser = FindAndActivateTab(tab);
    if (browser && browser->command_updater()->IsCommandEnabled(IDC_BACK)) {
      new NavigationNotificationObserver(tab, this, reply_message,
                                         number_of_navigations, false, false);
      browser->GoBack(CURRENT_TAB);
      return;
    }
  }

  AutomationMsg_GoBackBlockUntilNavigationsComplete::WriteReplyParams(
      reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
  Send(reply_message);
}

void TestingAutomationProvider::GoForwardBlockUntilNavigationsComplete(
    int handle, int number_of_navigations, IPC::Message* reply_message) {
  if (tab_tracker_->ContainsHandle(handle)) {
    NavigationController* tab = tab_tracker_->GetResource(handle);
    Browser* browser = FindAndActivateTab(tab);
    if (browser && browser->command_updater()->IsCommandEnabled(IDC_FORWARD)) {
      new NavigationNotificationObserver(tab, this, reply_message,
                                         number_of_navigations, false, false);
      browser->GoForward(CURRENT_TAB);
      return;
    }
  }

  AutomationMsg_GoForwardBlockUntilNavigationsComplete::WriteReplyParams(
      reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
  Send(reply_message);
}

void TestingAutomationProvider::SavePackageShouldPromptUser(
    bool should_prompt) {
  SavePackage::SetShouldPromptUser(should_prompt);
}

void TestingAutomationProvider::SetShelfVisibility(int handle, bool visible) {
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    if (browser) {
#if defined(OS_CHROMEOS)
      Browser* popup_browser =
          ActiveDownloadsUI::GetPopup(browser->profile());
      if (!popup_browser && visible)
        ActiveDownloadsUI::OpenPopup(browser->profile());
      if (popup_browser && !visible)
        popup_browser->CloseWindow();
#else
      if (visible)
        browser->window()->GetDownloadShelf()->Show();
      else
        browser->window()->GetDownloadShelf()->Close();
#endif
    }
  }
}

void TestingAutomationProvider::GetBlockedPopupCount(int handle, int* count) {
  *count = -1;  // -1 is the error code
  if (tab_tracker_->ContainsHandle(handle)) {
      NavigationController* nav_controller = tab_tracker_->GetResource(handle);
      TabContents* tab_contents = nav_controller->tab_contents();
      if (tab_contents) {
        BlockedContentContainer* container =
            tab_contents->blocked_content_container();
        if (container) {
          *count = static_cast<int>(container->GetBlockedContentsCount());
        } else {
          // If we don't have a container, we don't have any blocked popups to
          // contain!
          *count = 0;
        }
      }
  }
}

void TestingAutomationProvider::SendJSONRequest(int handle,
                                                const std::string& json_request,
                                                IPC::Message* reply_message) {
  scoped_ptr<Value> values;
  base::JSONReader reader;
  std::string error;
  values.reset(reader.ReadAndReturnError(json_request, true, NULL, &error));
  if (!error.empty()) {
    AutomationJSONReply(this, reply_message).SendError(error);
    return;
  }

  // Make sure input is a dict with a string command.
  std::string command;
  DictionaryValue* dict_value = NULL;
  if (values->GetType() != Value::TYPE_DICTIONARY) {
    AutomationJSONReply(this, reply_message).SendError("not a dict");
    return;
  }
  // Ownership remains with "values" variable.
  dict_value = static_cast<DictionaryValue*>(values.get());
  if (!dict_value->GetStringASCII(std::string("command"), &command)) {
    AutomationJSONReply(this, reply_message)
        .SendError("no command key in dict or not a string command");
    return;
  }

  // Map json commands to their handlers.
  std::map<std::string, JsonHandler> handler_map;
  handler_map["WaitForAllTabsToStopLoading"] =
      &TestingAutomationProvider::WaitForAllTabsToStopLoading;
  handler_map["GetIndicesFromTab"] =
      &TestingAutomationProvider::GetIndicesFromTab;
  handler_map["NavigateToURL"] =
      &TestingAutomationProvider::NavigateToURL;
  handler_map["ExecuteJavascript"] =
      &TestingAutomationProvider::ExecuteJavascriptJSON;
  handler_map["GoForward"] =
      &TestingAutomationProvider::GoForward;
  handler_map["GoBack"] =
      &TestingAutomationProvider::GoBack;
  handler_map["Reload"] =
      &TestingAutomationProvider::ReloadJSON;
  handler_map["GetTabURL"] =
      &TestingAutomationProvider::GetTabURLJSON;
  handler_map["GetTabTitle"] =
      &TestingAutomationProvider::GetTabTitleJSON;
  handler_map["CaptureEntirePage"] =
      &TestingAutomationProvider::CaptureEntirePageJSON;
  handler_map["GetCookies"] =
      &TestingAutomationProvider::GetCookiesJSON;
  handler_map["DeleteCookie"] =
      &TestingAutomationProvider::DeleteCookieJSON;
  handler_map["SetCookie"] =
      &TestingAutomationProvider::SetCookieJSON;
  handler_map["GetTabIds"] =
      &TestingAutomationProvider::GetTabIds;
  handler_map["IsTabIdValid"] =
      &TestingAutomationProvider::IsTabIdValid;
  handler_map["CloseTab"] =
      &TestingAutomationProvider::CloseTabJSON;
  handler_map["WebkitMouseMove"] =
      &TestingAutomationProvider::WebkitMouseMove;
  handler_map["WebkitMouseClick"] =
      &TestingAutomationProvider::WebkitMouseClick;
  handler_map["WebkitMouseDrag"] =
      &TestingAutomationProvider::WebkitMouseDrag;
  handler_map["SendWebkitKeyEvent"] =
      &TestingAutomationProvider::SendWebkitKeyEvent;
  handler_map["SendOSLevelKeyEventToTab"] =
      &TestingAutomationProvider::SendOSLevelKeyEventToTab;
  handler_map["ActivateTab"] =
      &TestingAutomationProvider::ActivateTabJSON;
  handler_map["UpdateExtensionsNow"] =
      &TestingAutomationProvider::UpdateExtensionsNow;
  handler_map["GetChromeDriverAutomationVersion"] =
      &TestingAutomationProvider::GetChromeDriverAutomationVersion;
#if defined(OS_CHROMEOS)
  handler_map["GetLoginInfo"] = &TestingAutomationProvider::GetLoginInfo;
  handler_map["LoginAsGuest"] = &TestingAutomationProvider::LoginAsGuest;
  handler_map["Login"] = &TestingAutomationProvider::Login;

  handler_map["LockScreen"] = &TestingAutomationProvider::LockScreen;
  handler_map["UnlockScreen"] = &TestingAutomationProvider::UnlockScreen;
  handler_map["SignoutInScreenLocker"] =
      &TestingAutomationProvider::SignoutInScreenLocker;

  handler_map["GetBatteryInfo"] = &TestingAutomationProvider::GetBatteryInfo;

  handler_map["GetNetworkInfo"] = &TestingAutomationProvider::GetNetworkInfo;
  handler_map["NetworkScan"] = &TestingAutomationProvider::NetworkScan;
  handler_map["GetProxySettings"] =
      &TestingAutomationProvider::GetProxySettings;
  handler_map["SetProxySettings"] =
      &TestingAutomationProvider::SetProxySettings;
  handler_map["ConnectToWifiNetwork"] =
      &TestingAutomationProvider::ConnectToWifiNetwork;
  handler_map["ConnectToHiddenWifiNetwork"] =
      &TestingAutomationProvider::ConnectToHiddenWifiNetwork;
  handler_map["DisconnectFromWifiNetwork"] =
      &TestingAutomationProvider::DisconnectFromWifiNetwork;

  handler_map["GetUpdateInfo"] = &TestingAutomationProvider::GetUpdateInfo;
  handler_map["UpdateCheck"] = &TestingAutomationProvider::UpdateCheck;
  handler_map["SetReleaseTrack"] = &TestingAutomationProvider::SetReleaseTrack;
#endif  // defined(OS_CHROMEOS)

  std::map<std::string, BrowserJsonHandler> browser_handler_map;
  browser_handler_map["DisablePlugin"] =
      &TestingAutomationProvider::DisablePlugin;
  browser_handler_map["EnablePlugin"] =
      &TestingAutomationProvider::EnablePlugin;
  browser_handler_map["GetPluginsInfo"] =
      &TestingAutomationProvider::GetPluginsInfo;

  browser_handler_map["GetBrowserInfo"] =
      &TestingAutomationProvider::GetBrowserInfo;

  browser_handler_map["GetNavigationInfo"] =
      &TestingAutomationProvider::GetNavigationInfo;

  browser_handler_map["PerformActionOnInfobar"] =
      &TestingAutomationProvider::PerformActionOnInfobar;

  browser_handler_map["GetHistoryInfo"] =
      &TestingAutomationProvider::GetHistoryInfo;
  browser_handler_map["AddHistoryItem"] =
      &TestingAutomationProvider::AddHistoryItem;

  browser_handler_map["GetOmniboxInfo"] =
      &TestingAutomationProvider::GetOmniboxInfo;
  browser_handler_map["SetOmniboxText"] =
      &TestingAutomationProvider::SetOmniboxText;
  browser_handler_map["OmniboxAcceptInput"] =
      &TestingAutomationProvider::OmniboxAcceptInput;
  browser_handler_map["OmniboxMovePopupSelection"] =
      &TestingAutomationProvider::OmniboxMovePopupSelection;

  browser_handler_map["GetInstantInfo"] =
      &TestingAutomationProvider::GetInstantInfo;

  browser_handler_map["LoadSearchEngineInfo"] =
      &TestingAutomationProvider::LoadSearchEngineInfo;
  browser_handler_map["GetSearchEngineInfo"] =
      &TestingAutomationProvider::GetSearchEngineInfo;
  browser_handler_map["AddOrEditSearchEngine"] =
      &TestingAutomationProvider::AddOrEditSearchEngine;
  browser_handler_map["PerformActionOnSearchEngine"] =
      &TestingAutomationProvider::PerformActionOnSearchEngine;

  browser_handler_map["GetPrefsInfo"] =
      &TestingAutomationProvider::GetPrefsInfo;
  browser_handler_map["SetPrefs"] = &TestingAutomationProvider::SetPrefs;

  browser_handler_map["SetWindowDimensions"] =
      &TestingAutomationProvider::SetWindowDimensions;

  browser_handler_map["GetDownloadsInfo"] =
      &TestingAutomationProvider::GetDownloadsInfo;
  browser_handler_map["WaitForAllDownloadsToComplete"] =
      &TestingAutomationProvider::WaitForDownloadsToComplete;
  browser_handler_map["PerformActionOnDownload"] =
      &TestingAutomationProvider::PerformActionOnDownload;

  browser_handler_map["GetInitialLoadTimes"] =
      &TestingAutomationProvider::GetInitialLoadTimes;

  browser_handler_map["SaveTabContents"] =
      &TestingAutomationProvider::SaveTabContents;

  browser_handler_map["ImportSettings"] =
      &TestingAutomationProvider::ImportSettings;

  browser_handler_map["AddSavedPassword"] =
      &TestingAutomationProvider::AddSavedPassword;
  browser_handler_map["RemoveSavedPassword"] =
      &TestingAutomationProvider::RemoveSavedPassword;
  browser_handler_map["GetSavedPasswords"] =
      &TestingAutomationProvider::GetSavedPasswords;

  browser_handler_map["ClearBrowsingData"] =
      &TestingAutomationProvider::ClearBrowsingData;

  browser_handler_map["GetBlockedPopupsInfo"] =
      &TestingAutomationProvider::GetBlockedPopupsInfo;
  browser_handler_map["UnblockAndLaunchBlockedPopup"] =
      &TestingAutomationProvider::UnblockAndLaunchBlockedPopup;

  // SetTheme() implemented using InstallExtension().
  browser_handler_map["GetThemeInfo"] =
      &TestingAutomationProvider::GetThemeInfo;

  // InstallExtension() present in pyauto.py.
  browser_handler_map["GetExtensionsInfo"] =
      &TestingAutomationProvider::GetExtensionsInfo;
  browser_handler_map["UninstallExtensionById"] =
      &TestingAutomationProvider::UninstallExtensionById;

  browser_handler_map["FindInPage"] = &TestingAutomationProvider::FindInPage;

  browser_handler_map["SelectTranslateOption"] =
      &TestingAutomationProvider::SelectTranslateOption;
  browser_handler_map["GetTranslateInfo"] =
      &TestingAutomationProvider::GetTranslateInfo;

  browser_handler_map["GetAutofillProfile"] =
      &TestingAutomationProvider::GetAutofillProfile;
  browser_handler_map["FillAutofillProfile"] =
      &TestingAutomationProvider::FillAutofillProfile;

  browser_handler_map["GetActiveNotifications"] =
      &TestingAutomationProvider::GetActiveNotifications;
  browser_handler_map["CloseNotification"] =
      &TestingAutomationProvider::CloseNotification;
  browser_handler_map["WaitForNotificationCount"] =
      &TestingAutomationProvider::WaitForNotificationCount;

  browser_handler_map["SignInToSync"] =
      &TestingAutomationProvider::SignInToSync;
  browser_handler_map["GetSyncInfo"] = &TestingAutomationProvider::GetSyncInfo;
  browser_handler_map["AwaitSyncCycleCompletion"] =
      &TestingAutomationProvider::AwaitSyncCycleCompletion;
  browser_handler_map["EnableSyncForDatatypes"] =
      &TestingAutomationProvider::EnableSyncForDatatypes;
  browser_handler_map["DisableSyncForDatatypes"] =
      &TestingAutomationProvider::DisableSyncForDatatypes;

  browser_handler_map["GetNTPInfo"] =
      &TestingAutomationProvider::GetNTPInfo;
  browser_handler_map["MoveNTPMostVisitedThumbnail"] =
      &TestingAutomationProvider::MoveNTPMostVisitedThumbnail;
  browser_handler_map["RemoveNTPMostVisitedThumbnail"] =
      &TestingAutomationProvider::RemoveNTPMostVisitedThumbnail;
  browser_handler_map["UnpinNTPMostVisitedThumbnail"] =
      &TestingAutomationProvider::UnpinNTPMostVisitedThumbnail;
  browser_handler_map["RestoreAllNTPMostVisitedThumbnails"] =
      &TestingAutomationProvider::RestoreAllNTPMostVisitedThumbnails;

  browser_handler_map["KillRendererProcess"] =
      &TestingAutomationProvider::KillRendererProcess;

  browser_handler_map["GetNTPThumbnailMode"] =
      &TestingAutomationProvider::GetNTPThumbnailMode;
  browser_handler_map["SetNTPThumbnailMode"] =
      &TestingAutomationProvider::SetNTPThumbnailMode;
  browser_handler_map["GetNTPMenuMode"] =
      &TestingAutomationProvider::GetNTPMenuMode;
  browser_handler_map["SetNTPMenuMode"] =
      &TestingAutomationProvider::SetNTPMenuMode;

  browser_handler_map["LaunchApp"] = &TestingAutomationProvider::LaunchApp;
  browser_handler_map["SetAppLaunchType"] =
      &TestingAutomationProvider::SetAppLaunchType;

  if (handler_map.find(std::string(command)) != handler_map.end()) {
    (this->*handler_map[command])(dict_value, reply_message);
  } else if (browser_handler_map.find(std::string(command)) !=
             browser_handler_map.end()) {
    Browser* browser = NULL;
    if (!browser_tracker_->ContainsHandle(handle) ||
        !(browser = browser_tracker_->GetResource(handle))) {
      AutomationJSONReply(this, reply_message).SendError("No browser object.");
      return;
    }
    (this->*browser_handler_map[command])(browser, dict_value, reply_message);
  } else {
    std::string error_string = "Unknown command. Options: ";
    for (std::map<std::string, JsonHandler>::const_iterator it =
         handler_map.begin(); it != handler_map.end(); ++it) {
      error_string += it->first + ", ";
    }
    for (std::map<std::string, BrowserJsonHandler>::const_iterator it =
         browser_handler_map.begin(); it != browser_handler_map.end(); ++it) {
      error_string += it->first + ", ";
    }
    AutomationJSONReply(this, reply_message).SendError(error_string);
  }
}

// Sample json input: { "command": "SetWindowDimensions",
//                      "x": 20,         # optional
//                      "y": 20,         # optional
//                      "width": 800,    # optional
//                      "height": 600 }  # optional
void TestingAutomationProvider::SetWindowDimensions(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  gfx::Rect rect = browser->window()->GetRestoredBounds();
  int x, y, width, height;
  if (args->GetInteger("x", &x))
    rect.set_x(x);
  if (args->GetInteger("y", &y))
    rect.set_y(y);
  if (args->GetInteger("width", &width))
    rect.set_width(width);
  if (args->GetInteger("height", &height))
    rect.set_height(height);
  browser->window()->SetBounds(rect);
  AutomationJSONReply(this, reply_message).SendSuccess(NULL);
}

ListValue* TestingAutomationProvider::GetInfobarsInfo(TabContents* tc) {
  // Each infobar may have different properties depending on the type.
  ListValue* infobars = new ListValue;
  for (size_t i = 0; i < tc->infobar_count(); ++i) {
    DictionaryValue* infobar_item = new DictionaryValue;
    InfoBarDelegate* infobar = tc->GetInfoBarDelegateAt(i);
    if (infobar->AsConfirmInfoBarDelegate()) {
      // Also covers ThemeInstalledInfoBarDelegate.
      infobar_item->SetString("type", "confirm_infobar");
      ConfirmInfoBarDelegate* confirm_infobar =
        infobar->AsConfirmInfoBarDelegate();
      infobar_item->SetString("text", confirm_infobar->GetMessageText());
      infobar_item->SetString("link_text", confirm_infobar->GetLinkText());
      ListValue* buttons_list = new ListValue;
      int buttons = confirm_infobar->GetButtons();
      if (buttons & ConfirmInfoBarDelegate::BUTTON_OK) {
        StringValue* button_label = new StringValue(
            confirm_infobar->GetButtonLabel(
              ConfirmInfoBarDelegate::BUTTON_OK));
        buttons_list->Append(button_label);
      }
      if (buttons & ConfirmInfoBarDelegate::BUTTON_CANCEL) {
        StringValue* button_label = new StringValue(
            confirm_infobar->GetButtonLabel(
              ConfirmInfoBarDelegate::BUTTON_CANCEL));
        buttons_list->Append(button_label);
      }
      infobar_item->Set("buttons", buttons_list);
    } else if (infobar->AsLinkInfoBarDelegate()) {
      infobar_item->SetString("type", "link_infobar");
      LinkInfoBarDelegate* link_infobar = infobar->AsLinkInfoBarDelegate();
      infobar_item->SetString("link_text", link_infobar->GetLinkText());
    } else if (infobar->AsTranslateInfoBarDelegate()) {
      infobar_item->SetString("type", "translate_infobar");
      TranslateInfoBarDelegate* translate_infobar =
          infobar->AsTranslateInfoBarDelegate();
      infobar_item->SetString("original_lang_code",
                              translate_infobar->GetOriginalLanguageCode());
      infobar_item->SetString("target_lang_code",
                              translate_infobar->GetTargetLanguageCode());
    } else if (infobar->AsExtensionInfoBarDelegate()) {
      infobar_item->SetString("type", "extension_infobar");
    } else {
      infobar_item->SetString("type", "unknown_infobar");
    }
    infobars->Append(infobar_item);
  }
  return infobars;
}

// Sample json input: { "command": "PerformActionOnInfobar",
//                      "action": "dismiss",
//                      "infobar_index": 0,
//                      "tab_index": 0 }
// Sample output: {}
void TestingAutomationProvider::PerformActionOnInfobar(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  int tab_index;
  int infobar_index_int;
  std::string action;
  if (!args->GetInteger("tab_index", &tab_index) ||
      !args->GetInteger("infobar_index", &infobar_index_int) ||
      !args->GetString("action", &action)) {
    reply.SendError("Invalid or missing args");
    return;
  }
  TabContents* tab_contents = browser->GetTabContentsAt(tab_index);
  if (!tab_contents) {
    reply.SendError(StringPrintf("No such tab at index %d", tab_index));
    return;
  }
  InfoBarDelegate* infobar = NULL;
  size_t infobar_index = static_cast<size_t>(infobar_index_int);
  if (infobar_index >= tab_contents->infobar_count() ||
      !(infobar = tab_contents->GetInfoBarDelegateAt(infobar_index))) {
    reply.SendError(StringPrintf("No such infobar at index %" PRIuS,
                                 infobar_index));
    return;
  }
  if ("dismiss" == action) {
    infobar->InfoBarDismissed();
    tab_contents->RemoveInfoBar(infobar);
    reply.SendSuccess(NULL);
    return;
  }
  if ("accept" == action || "cancel" == action) {
    ConfirmInfoBarDelegate* confirm_infobar;
    if (!(confirm_infobar = infobar->AsConfirmInfoBarDelegate())) {
      reply.SendError("Not a confirm infobar");
      return;
    }
    if ("accept" == action) {
      if (confirm_infobar->Accept())
        tab_contents->RemoveInfoBar(infobar);
    } else if ("cancel" == action) {
      if (confirm_infobar->Cancel())
        tab_contents->RemoveInfoBar(infobar);
    }
    reply.SendSuccess(NULL);
    return;
  }
  reply.SendError("Invalid action");
}

namespace {

// Task to get info about BrowserChildProcessHost. Must run on IO thread to
// honor the semantics of BrowserChildProcessHost.
// Used by AutomationProvider::GetBrowserInfo().
class GetChildProcessHostInfoTask : public Task {
 public:
  GetChildProcessHostInfoTask(base::WaitableEvent* event,
                              ListValue* child_processes)
    : event_(event),
      child_processes_(child_processes) {}

  virtual void Run() {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    for (BrowserChildProcessHost::Iterator iter; !iter.Done(); ++iter) {
      // Only add processes which are already started,
      // since we need their handle.
      if ((*iter)->handle() == base::kNullProcessHandle) {
        continue;
      }
      ChildProcessInfo* info = *iter;
      DictionaryValue* item = new DictionaryValue;
      item->SetString("name", WideToUTF16Hack(info->name()));
      item->SetString("type",
                      ChildProcessInfo::GetTypeNameInEnglish(info->type()));
      item->SetInteger("pid", base::GetProcId(info->handle()));
      child_processes_->Append(item);
    }
    event_->Signal();
  }

 private:
  base::WaitableEvent* const event_;  // weak
  ListValue* child_processes_;

  DISALLOW_COPY_AND_ASSIGN(GetChildProcessHostInfoTask);
};

}  // namespace

// Sample json input: { "command": "GetBrowserInfo" }
// Refer to GetBrowserInfo() in chrome/test/pyautolib/pyauto.py for
// sample json output.
void TestingAutomationProvider::GetBrowserInfo(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  base::ThreadRestrictions::ScopedAllowIO allow_io;  // needed for PathService
  DictionaryValue* properties = new DictionaryValue;
  properties->SetString("ChromeVersion", chrome::kChromeVersion);
  properties->SetString("BrowserProcessExecutableName",
                        chrome::kBrowserProcessExecutableName);
  properties->SetString("HelperProcessExecutableName",
                        chrome::kHelperProcessExecutableName);
  properties->SetString("BrowserProcessExecutablePath",
                        chrome::kBrowserProcessExecutablePath);
  properties->SetString("HelperProcessExecutablePath",
                        chrome::kHelperProcessExecutablePath);
  properties->SetString("command_line_string",
      CommandLine::ForCurrentProcess()->command_line_string());
  FilePath dumps_path;
  PathService::Get(chrome::DIR_CRASH_DUMPS, &dumps_path);
  properties->SetString("DIR_CRASH_DUMPS", dumps_path.value());

  std::string branding;
#if defined(GOOGLE_CHROME_BUILD)
  branding = "Google Chrome";
#elif defined(CHROMIUM_BUILD)
  branding = "Chromium";
#else
  branding = "Unknown Branding";
#endif
  properties->SetString("branding", branding);

  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
  return_value->Set("properties", properties);

  return_value->SetInteger("browser_pid", base::GetCurrentProcId());
  // Add info about all windows in a list of dictionaries, one dictionary
  // item per window.
  ListValue* windows = new ListValue;
  int windex = 0;
  for (BrowserList::const_iterator it = BrowserList::begin();
       it != BrowserList::end();
       ++it, ++windex) {
    DictionaryValue* browser_item = new DictionaryValue;
    browser = *it;
    browser_item->SetInteger("index", windex);
    // Window properties
    gfx::Rect rect = browser->window()->GetRestoredBounds();
    browser_item->SetInteger("x", rect.x());
    browser_item->SetInteger("y", rect.y());
    browser_item->SetInteger("width", rect.width());
    browser_item->SetInteger("height", rect.height());
    browser_item->SetBoolean("fullscreen",
                             browser->window()->IsFullscreen());
    browser_item->SetInteger("selected_tab", browser->active_index());
    browser_item->SetBoolean("incognito",
                             browser->profile()->IsOffTheRecord());
    // For each window, add info about all tabs in a list of dictionaries,
    // one dictionary item per tab.
    ListValue* tabs = new ListValue;
    for (int i = 0; i < browser->tab_count(); ++i) {
      TabContents* tc = browser->GetTabContentsAt(i);
      DictionaryValue* tab = new DictionaryValue;
      tab->SetInteger("index", i);
      tab->SetString("url", tc->GetURL().spec());
      tab->SetInteger("renderer_pid",
                      base::GetProcId(tc->GetRenderProcessHost()->GetHandle()));
      tab->Set("infobars", GetInfobarsInfo(tc));
      tab->SetBoolean("pinned", browser->IsTabPinned(i));
      tabs->Append(tab);
    }
    browser_item->Set("tabs", tabs);

    windows->Append(browser_item);
  }
  return_value->Set("windows", windows);

  return_value->SetString("child_process_path",
                          ChildProcessHost::GetChildPath(true).value());
  // Child processes are the processes for plugins and other workers.
  // Add all child processes in a list of dictionaries, one dictionary item
  // per child process.
  ListValue* child_processes = new ListValue;
  base::WaitableEvent event(true   /* manual reset */,
                            false  /* not initially signaled */);
  CHECK(BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      new GetChildProcessHostInfoTask(&event, child_processes)));
  event.Wait();
  return_value->Set("child_processes", child_processes);

  // Add all extension processes in a list of dictionaries, one dictionary
  // item per extension process.
  ListValue* extension_processes = new ListValue;
  ProfileManager* profile_manager = g_browser_process->profile_manager();
  std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
  for (size_t i = 0; i < profiles.size(); ++i) {
    ExtensionProcessManager* process_manager =
        profiles[i]->GetExtensionProcessManager();
    if (!process_manager)
      continue;
    ExtensionProcessManager::const_iterator jt;
    for (jt = process_manager->begin(); jt != process_manager->end(); ++jt) {
      ExtensionHost* ex_host = *jt;
      // Don't add dead extension processes.
      if (!ex_host->IsRenderViewLive())
        continue;
      DictionaryValue* item = new DictionaryValue;
      item->SetString("name", ex_host->extension()->name());
      item->SetInteger(
          "pid",
          base::GetProcId(ex_host->render_process_host()->GetHandle()));
      extension_processes->Append(item);
    }
  }
  return_value->Set("extension_processes", extension_processes);
  AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
}

// Sample json input: { "command": "GetNavigationInfo" }
// Refer to GetNavigationInfo() in chrome/test/pyautolib/pyauto.py for
// sample json output.
void TestingAutomationProvider::GetNavigationInfo(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  int tab_index;
  TabContents* tab_contents = NULL;
  if (!args->GetInteger("tab_index", &tab_index) ||
      !(tab_contents = browser->GetTabContentsAt(tab_index))) {
    reply.SendError("tab_index missing or invalid.");
    return;
  }
  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
  const NavigationController& controller = tab_contents->controller();
  NavigationEntry* nav_entry = controller.GetActiveEntry();
  DCHECK(nav_entry);

  // Security info.
  DictionaryValue* ssl = new DictionaryValue;
  std::map<SecurityStyle, std::string> style_to_string;
  style_to_string[SECURITY_STYLE_UNKNOWN] = "SECURITY_STYLE_UNKNOWN";
  style_to_string[SECURITY_STYLE_UNAUTHENTICATED] =
      "SECURITY_STYLE_UNAUTHENTICATED";
  style_to_string[SECURITY_STYLE_AUTHENTICATION_BROKEN] =
      "SECURITY_STYLE_AUTHENTICATION_BROKEN";
  style_to_string[SECURITY_STYLE_AUTHENTICATED] =
      "SECURITY_STYLE_AUTHENTICATED";

  NavigationEntry::SSLStatus ssl_status = nav_entry->ssl();
  ssl->SetString("security_style",
                 style_to_string[ssl_status.security_style()]);
  ssl->SetBoolean("ran_insecure_content", ssl_status.ran_insecure_content());
  ssl->SetBoolean("displayed_insecure_content",
                  ssl_status.displayed_insecure_content());
  return_value->Set("ssl", ssl);

  // Page type.
  std::map<PageType, std::string> pagetype_to_string;
  pagetype_to_string[NORMAL_PAGE] = "NORMAL_PAGE";
  pagetype_to_string[ERROR_PAGE] = "ERROR_PAGE";
  pagetype_to_string[INTERSTITIAL_PAGE] = "INTERSTITIAL_PAGE";
  return_value->SetString("page_type",
                          pagetype_to_string[nav_entry->page_type()]);

  return_value->SetString("favicon_url", nav_entry->favicon().url().spec());
  reply.SendSuccess(return_value.get());
}

// Sample json input: { "command": "GetHistoryInfo",
//                      "search_text": "some text" }
// Refer chrome/test/pyautolib/history_info.py for sample json output.
void TestingAutomationProvider::GetHistoryInfo(Browser* browser,
                                               DictionaryValue* args,
                                               IPC::Message* reply_message) {
  consumer_.CancelAllRequests();

  string16 search_text;
  args->GetString("search_text", &search_text);

  // Fetch history.
  HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
  history::QueryOptions options;
  // The observer owns itself.  It deletes itself after it fetches history.
  AutomationProviderHistoryObserver* history_observer =
      new AutomationProviderHistoryObserver(this, reply_message);
  hs->QueryHistory(
      search_text,
      options,
      &consumer_,
      NewCallback(history_observer,
                  &AutomationProviderHistoryObserver::HistoryQueryComplete));
}

// Sample json input: { "command": "AddHistoryItem",
//                      "item": { "URL": "http://www.google.com",
//                                "title": "Google",   # optional
//                                "time": 12345        # optional (time_t)
//                               } }
// Refer chrome/test/pyautolib/pyauto.py for details on input.
void TestingAutomationProvider::AddHistoryItem(Browser* browser,
                                               DictionaryValue* args,
                                               IPC::Message* reply_message) {
  DictionaryValue* item = NULL;
  args->GetDictionary("item", &item);
  string16 url_text;
  string16 title;
  base::Time time = base::Time::Now();
  AutomationJSONReply reply(this, reply_message);

  if (!item->GetString("url", &url_text)) {
    reply.SendError("bad args (no URL in dict?)");
    return;
  }
  GURL gurl(url_text);
  item->GetString("title", &title);  // Don't care if it fails.
  int it;
  double dt;
  if (item->GetInteger("time", &it))
    time = base::Time::FromTimeT(it);
  else if (item->GetDouble("time", &dt))
    time = base::Time::FromDoubleT(dt);

  // Ideas for "dummy" values (e.g. id_scope) came from
  // chrome/browser/autocomplete/history_contents_provider_unittest.cc
  HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
  const void* id_scope = reinterpret_cast<void*>(1);
  hs->AddPage(gurl, time,
              id_scope,
              0,
              GURL(),
              PageTransition::LINK,
              history::RedirectList(),
              history::SOURCE_BROWSED,
              false);
  if (title.length())
    hs->SetPageTitle(gurl, title);
  reply.SendSuccess(NULL);
}

// Sample json input: { "command": "GetDownloadsInfo" }
// Refer chrome/test/pyautolib/download_info.py for sample json output.
void TestingAutomationProvider::GetDownloadsInfo(Browser* browser,
                                                 DictionaryValue* args,
                                                 IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
  ListValue* list_of_downloads = new ListValue;

  if (browser->profile()->HasCreatedDownloadManager()) {
    std::vector<DownloadItem*> downloads;
    browser->profile()->GetDownloadManager()->
        GetAllDownloads(FilePath(), &downloads);

    for (std::vector<DownloadItem*>::iterator it = downloads.begin();
         it != downloads.end();
         it++) {  // Fill info about each download item.
      list_of_downloads->Append(GetDictionaryFromDownloadItem(*it));
    }
  }
  return_value->Set("downloads", list_of_downloads);
  reply.SendSuccess(return_value.get());
}

void TestingAutomationProvider::WaitForDownloadsToComplete(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {

  // Look for a quick return.
  if (!browser->profile()->HasCreatedDownloadManager()) {
    // No download manager.
    AutomationJSONReply(this, reply_message).SendSuccess(NULL);
    return;
  }
  std::vector<DownloadItem*> downloads;
  browser->profile()->GetDownloadManager()->
      GetCurrentDownloads(FilePath(), &downloads);
  if (downloads.empty()) {
    AutomationJSONReply(this, reply_message).SendSuccess(NULL);
    return;
  }
  // The observer owns itself.  When the last observed item pings, it
  // deletes itself.
  AutomationProviderDownloadItemObserver* item_observer =
      new AutomationProviderDownloadItemObserver(
          this, reply_message, downloads.size());
  for (std::vector<DownloadItem*>::iterator i = downloads.begin();
       i != downloads.end();
       i++) {
    (*i)->AddObserver(item_observer);
  }
}

namespace {

DownloadItem* GetDownloadItemFromId(int id, DownloadManager* download_manager) {
  std::vector<DownloadItem*> downloads;
  download_manager->GetAllDownloads(FilePath(), &downloads);
  DownloadItem* selected_item = NULL;

  for (std::vector<DownloadItem*>::iterator it = downloads.begin();
       it != downloads.end();
       it++) {
    DownloadItem* curr_item = *it;
    if (curr_item->id() == id) {
      selected_item = curr_item;
      break;
    }
  }
  return selected_item;
}

}  // namespace

// See PerformActionOnDownload() in chrome/test/pyautolib/pyauto.py for sample
// json input and output.
void TestingAutomationProvider::PerformActionOnDownload(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  int id;
  std::string action;

  if (!browser->profile()->HasCreatedDownloadManager()) {
    AutomationJSONReply(this, reply_message).SendError("No download manager.");
    return;
  }
  if (!args->GetInteger("id", &id) || !args->GetString("action", &action)) {
    AutomationJSONReply(this, reply_message)
        .SendError("Must include int id and string action.");
    return;
  }

  DownloadManager* download_manager = browser->profile()->GetDownloadManager();
  DownloadItem* selected_item = GetDownloadItemFromId(id, download_manager);
  if (!selected_item) {
    AutomationJSONReply(this, reply_message)
        .SendError(StringPrintf("No download with an id of %d\n", id));
    return;
  }

  if (action == "open") {
    selected_item->AddObserver(
        new AutomationProviderDownloadUpdatedObserver(
            this, reply_message, true));
    selected_item->OpenDownload();
  } else if (action == "toggle_open_files_like_this") {
    selected_item->OpenFilesBasedOnExtension(
        !selected_item->ShouldOpenFileBasedOnExtension());
    AutomationJSONReply(this, reply_message).SendSuccess(NULL);
  } else if (action == "remove") {
    download_manager->AddObserver(
        new AutomationProviderDownloadModelChangedObserver(
            this, reply_message, download_manager));
    selected_item->Remove();
  } else if (action == "decline_dangerous_download") {
    download_manager->AddObserver(
        new AutomationProviderDownloadModelChangedObserver(
            this, reply_message, download_manager));
    selected_item->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD);
  } else if (action == "save_dangerous_download") {
    selected_item->AddObserver(new AutomationProviderDownloadUpdatedObserver(
        this, reply_message, false));
    selected_item->DangerousDownloadValidated();
  } else if (action == "toggle_pause") {
    selected_item->AddObserver(new AutomationProviderDownloadUpdatedObserver(
        this, reply_message, false));
    // This will still return if download has already completed.
    selected_item->TogglePause();
  } else if (action == "cancel") {
    selected_item->AddObserver(new AutomationProviderDownloadUpdatedObserver(
        this, reply_message, false));
    selected_item->Cancel(true);
  } else {
    AutomationJSONReply(this, reply_message)
        .SendError(StringPrintf("Invalid action '%s' given.", action.c_str()));
  }
}

// Sample JSON input { "command": "LoadSearchEngineInfo" }
void TestingAutomationProvider::LoadSearchEngineInfo(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  TemplateURLModel* url_model(profile_->GetTemplateURLModel());
  if (url_model->loaded()) {
    AutomationJSONReply(this, reply_message).SendSuccess(NULL);
    return;
  }
  url_model->AddObserver(new AutomationProviderSearchEngineObserver(
      this, reply_message));
  url_model->Load();
}

// Sample JSON input { "command": "GetSearchEngineInfo" }
// Refer to pyauto.py for sample output.
void TestingAutomationProvider::GetSearchEngineInfo(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  TemplateURLModel* url_model(profile_->GetTemplateURLModel());
  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
  ListValue* search_engines = new ListValue;
  std::vector<const TemplateURL*> template_urls = url_model->GetTemplateURLs();
  for (std::vector<const TemplateURL*>::const_iterator it =
       template_urls.begin(); it != template_urls.end(); ++it) {
    DictionaryValue* search_engine = new DictionaryValue;
    search_engine->SetString("short_name", UTF16ToUTF8((*it)->short_name()));
    search_engine->SetString("description", UTF16ToUTF8((*it)->description()));
    search_engine->SetString("keyword", UTF16ToUTF8((*it)->keyword()));
    search_engine->SetBoolean("in_default_list", (*it)->ShowInDefaultList());
    search_engine->SetBoolean("is_default",
        (*it) == url_model->GetDefaultSearchProvider());
    search_engine->SetBoolean("is_valid", (*it)->url()->IsValid());
    search_engine->SetBoolean("supports_replacement",
                              (*it)->url()->SupportsReplacement());
    search_engine->SetString("url", (*it)->url()->url());
    search_engine->SetString("host", (*it)->url()->GetHost());
    search_engine->SetString("path", (*it)->url()->GetPath());
    search_engine->SetString("display_url",
                             UTF16ToUTF8((*it)->url()->DisplayURL()));
    search_engines->Append(search_engine);
  }
  return_value->Set("search_engines", search_engines);
  AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
}

// Refer to pyauto.py for sample JSON input.
void TestingAutomationProvider::AddOrEditSearchEngine(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  TemplateURLModel* url_model(profile_->GetTemplateURLModel());
  const TemplateURL* template_url;
  string16 new_title;
  string16 new_keyword;
  std::string new_url;
  std::string keyword;
  if (!args->GetString("new_title", &new_title) ||
      !args->GetString("new_keyword", &new_keyword) ||
      !args->GetString("new_url", &new_url)) {
    AutomationJSONReply(this, reply_message)
        .SendError("One or more inputs invalid");
    return;
  }
  std::string new_ref_url = TemplateURLRef::DisplayURLToURLRef(
      UTF8ToUTF16(new_url));
  scoped_ptr<KeywordEditorController> controller(
      new KeywordEditorController(profile_));
  if (args->GetString("keyword", &keyword)) {
    template_url = url_model->GetTemplateURLForKeyword(UTF8ToUTF16(keyword));
    if (template_url == NULL) {
      AutomationJSONReply(this, reply_message)
          .SendError(StringPrintf("No match for keyword: %s", keyword.c_str()));
      return;
    }
    url_model->AddObserver(new AutomationProviderSearchEngineObserver(
        this, reply_message));
    controller->ModifyTemplateURL(template_url, new_title, new_keyword,
                                  new_ref_url);
  } else {
    url_model->AddObserver(new AutomationProviderSearchEngineObserver(
        this, reply_message));
    controller->AddTemplateURL(new_title, new_keyword, new_ref_url);
  }
}

// Sample json input: { "command": "PerformActionOnSearchEngine",
//                      "keyword": keyword, "action": action }
void TestingAutomationProvider::PerformActionOnSearchEngine(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  TemplateURLModel* url_model(profile_->GetTemplateURLModel());
  std::string keyword;
  std::string action;
  if (!args->GetString("keyword", &keyword) ||
      !args->GetString("action", &action)) {
    AutomationJSONReply(this, reply_message).SendError(
        "One or more inputs invalid");
    return;
  }
  const TemplateURL* template_url(
      url_model->GetTemplateURLForKeyword(UTF8ToUTF16(keyword)));
  if (template_url == NULL) {
    AutomationJSONReply(this, reply_message)
        .SendError(StringPrintf("No match for keyword: %s", keyword.c_str()));
    return;
  }
  if (action == "delete") {
    url_model->AddObserver(new AutomationProviderSearchEngineObserver(
      this, reply_message));
    url_model->Remove(template_url);
  } else if (action == "default") {
    url_model->AddObserver(new AutomationProviderSearchEngineObserver(
      this, reply_message));
    url_model->SetDefaultSearchProvider(template_url);
  } else {
    AutomationJSONReply(this, reply_message)
        .SendError(StringPrintf("Invalid action: %s", action.c_str()));
  }
}

// Sample json input: { "command": "GetPrefsInfo" }
// Refer chrome/test/pyautolib/prefs_info.py for sample json output.
void TestingAutomationProvider::GetPrefsInfo(Browser* browser,
                                             DictionaryValue* args,
                                             IPC::Message* reply_message) {
  DictionaryValue* items = profile_->GetPrefs()->GetPreferenceValues();

  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
  return_value->Set("prefs", items);  // return_value owns items.
  AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
}

// Sample json input: { "command": "SetPrefs", "path": path, "value": value }
void TestingAutomationProvider::SetPrefs(Browser* browser,
                                         DictionaryValue* args,
                                         IPC::Message* reply_message) {
  std::string path;
  Value* val;
  AutomationJSONReply reply(this, reply_message);
  if (args->GetString("path", &path) && args->Get("value", &val)) {
    PrefService* pref_service = profile_->GetPrefs();
    const PrefService::Preference* pref =
        pref_service->FindPreference(path.c_str());
    if (!pref) {  // Not a registered pref.
      reply.SendError("pref not registered.");
      return;
    } else if (pref->IsManaged()) {  // Do not attempt to change a managed pref.
      reply.SendError("pref is managed. cannot be changed.");
      return;
    } else {  // Set the pref.
      pref_service->Set(path.c_str(), *val);
    }
  } else {
    reply.SendError("no pref path or value given.");
    return;
  }

  reply.SendSuccess(NULL);
}

// Sample json input: { "command": "GetOmniboxInfo" }
// Refer chrome/test/pyautolib/omnibox_info.py for sample json output.
void TestingAutomationProvider::GetOmniboxInfo(Browser* browser,
                                               DictionaryValue* args,
                                               IPC::Message* reply_message) {
  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);

  LocationBar* loc_bar = browser->window()->GetLocationBar();
  AutocompleteEditView* edit_view = loc_bar->location_entry();
  AutocompleteEditModel* model = edit_view->model();

  // Fill up matches.
  ListValue* matches = new ListValue;
  const AutocompleteResult& result = model->result();
  for (AutocompleteResult::const_iterator i = result.begin();
       i != result.end(); ++i) {
    const AutocompleteMatch& match = *i;
    DictionaryValue* item = new DictionaryValue;  // owned by return_value
    item->SetString("type", AutocompleteMatch::TypeToString(match.type));
    item->SetBoolean("starred", match.starred);
    item->SetString("destination_url", match.destination_url.spec());
    item->SetString("contents", match.contents);
    item->SetString("description", match.description);
    matches->Append(item);
  }
  return_value->Set("matches", matches);

  // Fill up other properties.
  DictionaryValue* properties = new DictionaryValue;  // owned by return_value
  properties->SetBoolean("has_focus", model->has_focus());
  properties->SetBoolean("query_in_progress",
                         !model->autocomplete_controller()->done());
  properties->SetString("keyword", model->keyword());
  properties->SetString("text", edit_view->GetText());
  return_value->Set("properties", properties);

  AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
}

// Sample json input: { "command": "SetOmniboxText",
//                      "text": "goog" }
void TestingAutomationProvider::SetOmniboxText(Browser* browser,
                                               DictionaryValue* args,
                                               IPC::Message* reply_message) {
  string16 text;
  AutomationJSONReply reply(this, reply_message);
  if (!args->GetString("text", &text)) {
    reply.SendError("text missing");
    return;
  }
  browser->FocusLocationBar();
  LocationBar* loc_bar = browser->window()->GetLocationBar();
  AutocompleteEditView* edit_view = loc_bar->location_entry();
  edit_view->model()->OnSetFocus(false);
  edit_view->SetUserText(text);
  reply.SendSuccess(NULL);
}

// Sample json input: { "command": "OmniboxMovePopupSelection",
//                      "count": 1 }
// Negative count implies up, positive implies down. Count values will be
// capped by the size of the popup list.
void TestingAutomationProvider::OmniboxMovePopupSelection(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  int count;
  AutomationJSONReply reply(this, reply_message);
  if (!args->GetInteger("count", &count)) {
    reply.SendError("count missing");
    return;
  }
  LocationBar* loc_bar = browser->window()->GetLocationBar();
  AutocompleteEditModel* model = loc_bar->location_entry()->model();
  model->OnUpOrDownKeyPressed(count);
  reply.SendSuccess(NULL);
}

// Sample json input: { "command": "OmniboxAcceptInput" }
void TestingAutomationProvider::OmniboxAcceptInput(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  NavigationController& controller =
      browser->GetSelectedTabContents()->controller();
  new OmniboxAcceptNotificationObserver(&controller, this, reply_message);
  browser->window()->GetLocationBar()->AcceptInput();
}

// Sample json input: { "command": "GetInstantInfo" }
void TestingAutomationProvider::GetInstantInfo(Browser* browser,
                                               DictionaryValue* args,
                                               IPC::Message* reply_message) {
  DictionaryValue* info = new DictionaryValue;
  if (browser->instant()) {
    InstantController* instant = browser->instant();
    info->SetBoolean("enabled", true);
    info->SetBoolean("showing", instant->IsShowingInstant());
    info->SetBoolean("active", instant->is_active());
    info->SetBoolean("current", instant->IsCurrent());
    if (instant->GetPreviewContents() &&
        instant->GetPreviewContents()->tab_contents()) {
      TabContents* contents = instant->GetPreviewContents()->tab_contents();
      info->SetBoolean("loading", contents->is_loading());
      info->SetString("location", contents->GetURL().spec());
      info->SetString("title", contents->GetTitle());
    }
  } else {
    info->SetBoolean("enabled", false);
  }
  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
  return_value->Set("instant", info);
  AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
}

// Sample json input: { "command": "GetInitialLoadTimes" }
// Refer to InitialLoadObserver::GetTimingInformation() for sample output.
void TestingAutomationProvider::GetInitialLoadTimes(
    Browser*,
    DictionaryValue*,
    IPC::Message* reply_message) {
  scoped_ptr<DictionaryValue> return_value(
      initial_load_observer_->GetTimingInformation());

  std::string json_return;
  base::JSONWriter::Write(return_value.get(), false, &json_return);
  AutomationMsg_SendJSONRequest::WriteReplyParams(
      reply_message, json_return, true);
  Send(reply_message);
}

// Sample json input: { "command": "GetPluginsInfo" }
// Refer chrome/test/pyautolib/plugins_info.py for sample json output.
void TestingAutomationProvider::GetPluginsInfo(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  std::vector<webkit::npapi::WebPluginInfo> plugins;
  webkit::npapi::PluginList::Singleton()->GetPlugins(false, &plugins);
  ListValue* items = new ListValue;
  for (std::vector<webkit::npapi::WebPluginInfo>::const_iterator it =
           plugins.begin();
       it != plugins.end();
       ++it) {
    DictionaryValue* item = new DictionaryValue;
    item->SetString("name", it->name);
    item->SetString("path", it->path.value());
    item->SetString("version", it->version);
    item->SetString("desc", it->desc);
    item->SetBoolean("enabled", webkit::npapi::IsPluginEnabled(*it));
    // Add info about mime types.
    ListValue* mime_types = new ListValue();
    for (std::vector<webkit::npapi::WebPluginMimeType>::const_iterator type_it =
             it->mime_types.begin();
         type_it != it->mime_types.end();
         ++type_it) {
      DictionaryValue* mime_type = new DictionaryValue();
      mime_type->SetString("mimeType", type_it->mime_type);
      mime_type->SetString("description", type_it->description);

      ListValue* file_extensions = new ListValue();
      for (std::vector<std::string>::const_iterator ext_it =
               type_it->file_extensions.begin();
           ext_it != type_it->file_extensions.end();
           ++ext_it) {
        file_extensions->Append(new StringValue(*ext_it));
      }
      mime_type->Set("fileExtensions", file_extensions);

      mime_types->Append(mime_type);
    }
    item->Set("mimeTypes", mime_types);
    items->Append(item);
  }
  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
  return_value->Set("plugins", items);  // return_value owns items.

  AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
}

// Sample json input:
//    { "command": "EnablePlugin",
//      "path": "/Library/Internet Plug-Ins/Flash Player.plugin" }
void TestingAutomationProvider::EnablePlugin(Browser* browser,
                                             DictionaryValue* args,
                                             IPC::Message* reply_message) {
  FilePath::StringType path;
  AutomationJSONReply reply(this, reply_message);
  if (!args->GetString("path", &path)) {
    reply.SendError("path not specified.");
    return;
  } else if (!webkit::npapi::PluginList::Singleton()->EnablePlugin(
        FilePath(path))) {
    reply.SendError(StringPrintf("Could not enable plugin for path %s.",
                                 path.c_str()));
    return;
  }
  reply.SendSuccess(NULL);
}

// Sample json input:
//    { "command": "DisablePlugin",
//      "path": "/Library/Internet Plug-Ins/Flash Player.plugin" }
void TestingAutomationProvider::DisablePlugin(Browser* browser,
                                              DictionaryValue* args,
                                              IPC::Message* reply_message) {
  FilePath::StringType path;
  AutomationJSONReply reply(this, reply_message);
  if (!args->GetString("path", &path)) {
    reply.SendError("path not specified.");
    return;
  } else if (!webkit::npapi::PluginList::Singleton()->DisablePlugin(
        FilePath(path))) {
    reply.SendError(StringPrintf("Could not disable plugin for path %s.",
                                 path.c_str()));
    return;
  }
  reply.SendSuccess(NULL);
}

// Sample json input:
//    { "command": "SaveTabContents",
//      "tab_index": 0,
//      "filename": <a full pathname> }
// Sample json output:
//    {}
void TestingAutomationProvider::SaveTabContents(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  int tab_index = 0;
  FilePath::StringType filename;
  FilePath::StringType parent_directory;
  TabContentsWrapper* tab_contents = NULL;

  if (!args->GetInteger("tab_index", &tab_index) ||
      !args->GetString("filename", &filename)) {
    AutomationJSONReply(this, reply_message)
        .SendError("tab_index or filename param missing");
    return;
  } else {
    tab_contents = browser->GetTabContentsWrapperAt(tab_index);
    if (!tab_contents) {
      AutomationJSONReply(this, reply_message).SendError("no tab at tab_index");
      return;
    }
  }
  // We're doing a SAVE_AS_ONLY_HTML so the the directory path isn't
  // used.  Nevertheless, SavePackage requires it be valid.  Sigh.
  parent_directory = FilePath(filename).DirName().value();
  if (!tab_contents->download_tab_helper()->SavePage(
          FilePath(filename),
          FilePath(parent_directory),
          SavePackage::SAVE_AS_ONLY_HTML)) {
    AutomationJSONReply(this, reply_message).SendError(
        "Could not initiate SavePage");
    return;
  }
  // The observer will delete itself when done.
  new SavePackageNotificationObserver(
      tab_contents->download_tab_helper()->save_package(), this, reply_message);
}

// Refer to ImportSettings() in chrome/test/pyautolib/pyauto.py for sample
// json input.
// Sample json output: "{}"
void TestingAutomationProvider::ImportSettings(Browser* browser,
                                               DictionaryValue* args,
                                               IPC::Message* reply_message) {
  // Map from the json string passed over to the import item masks.
  std::map<std::string, importer::ImportItem> string_to_import_item;
  string_to_import_item["HISTORY"] = importer::HISTORY;
  string_to_import_item["FAVORITES"] = importer::FAVORITES;
  string_to_import_item["COOKIES"] = importer::COOKIES;
  string_to_import_item["PASSWORDS"] = importer::PASSWORDS;
  string_to_import_item["SEARCH_ENGINES"] = importer::SEARCH_ENGINES;
  string_to_import_item["HOME_PAGE"] = importer::HOME_PAGE;
  string_to_import_item["ALL"] = importer::ALL;

  ListValue* import_items_list = NULL;
  if (!args->GetString("import_from", &import_settings_data_.browser_name) ||
      !args->GetBoolean("first_run", &import_settings_data_.first_run) ||
      !args->GetList("import_items", &import_items_list)) {
    AutomationJSONReply(this, reply_message)
        .SendError("Incorrect type for one or more of the arguments.");
    return;
  }

  import_settings_data_.import_items = 0;
  int num_items = import_items_list->GetSize();
  for (int i = 0; i < num_items; i++) {
    std::string item;
    import_items_list->GetString(i, &item);
    // If the provided string is not part of the map, error out.
    if (!ContainsKey(string_to_import_item, item)) {
      AutomationJSONReply(this, reply_message)
          .SendError("Invalid item string found in import_items.");
      return;
    }
    import_settings_data_.import_items |= string_to_import_item[item];
  }

  import_settings_data_.browser = browser;
  import_settings_data_.reply_message = reply_message;

  importer_list_ = new ImporterList;
  importer_list_->DetectSourceProfiles(this);
}

namespace {

// Translates a dictionary password to a PasswordForm struct.
webkit_glue::PasswordForm GetPasswordFormFromDict(
    const DictionaryValue& password_dict) {

  // If the time is specified, change time to the specified time.
  base::Time time = base::Time::Now();
  int it;
  double dt;
  if (password_dict.GetInteger("time", &it))
    time = base::Time::FromTimeT(it);
  else if (password_dict.GetDouble("time", &dt))
    time = base::Time::FromDoubleT(dt);

  std::string signon_realm;
  string16 username_value;
  string16 password_value;
  string16 origin_url_text;
  string16 username_element;
  string16 password_element;
  string16 submit_element;
  string16 action_target_text;
  bool blacklist = false;
  string16 old_password_element;
  string16 old_password_value;

  // We don't care if any of these fail - they are either optional or checked
  // before this function is called.
  password_dict.GetString("signon_realm", &signon_realm);
  password_dict.GetString("username_value", &username_value);
  password_dict.GetString("password_value", &password_value);
  password_dict.GetString("origin_url", &origin_url_text);
  password_dict.GetString("username_element", &username_element);
  password_dict.GetString("password_element", &password_element);
  password_dict.GetString("submit_element", &submit_element);
  password_dict.GetString("action_target", &action_target_text);
  password_dict.GetBoolean("blacklist", &blacklist);

  GURL origin_gurl(origin_url_text);
  GURL action_target(action_target_text);

  webkit_glue::PasswordForm password_form;
  password_form.signon_realm = signon_realm;
  password_form.username_value = username_value;
  password_form.password_value = password_value;
  password_form.origin = origin_gurl;
  password_form.username_element = username_element;
  password_form.password_element = password_element;
  password_form.submit_element = submit_element;
  password_form.action = action_target;
  password_form.blacklisted_by_user = blacklist;
  password_form.date_created = time;

  return password_form;
}

}  // namespace

// See AddSavedPassword() in chrome/test/functional/pyauto.py for sample json
// input.
// Sample json output: { "password_added": true }
void TestingAutomationProvider::AddSavedPassword(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  DictionaryValue* password_dict = NULL;

  if (!args->GetDictionary("password", &password_dict)) {
    reply.SendError("Password must be a dictionary.");
    return;
  }

  // The signon realm is effectively the primary key and must be included.
  // Check here before calling GetPasswordFormFromDict.
  if (!password_dict->HasKey("signon_realm")) {
    reply.SendError("Password must include signon_realm.");
    return;
  }
  webkit_glue::PasswordForm new_password =
      GetPasswordFormFromDict(*password_dict);

  Profile* profile = browser->profile();
  // Use IMPLICIT_ACCESS since new passwords aren't added in incognito mode.
  PasswordStore* password_store =
      profile->GetPasswordStore(Profile::IMPLICIT_ACCESS);

  // Set the return based on whether setting the password succeeded.
  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);

  // It will be null if it's accessed in an incognito window.
  if (password_store != NULL) {
    password_store->AddLogin(new_password);
    return_value->SetBoolean("password_added", true);
  } else {
    return_value->SetBoolean("password_added", false);
  }

  reply.SendSuccess(return_value.get());
}

// See RemoveSavedPassword() in chrome/test/functional/pyauto.py for sample
// json input.
// Sample json output: {}
void TestingAutomationProvider::RemoveSavedPassword(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  DictionaryValue* password_dict = NULL;

  if (!args->GetDictionary("password", &password_dict)) {
    reply.SendError("Password must be a dictionary.");
    return;
  }

  // The signon realm is effectively the primary key and must be included.
  // Check here before calling GetPasswordFormFromDict.
  if (!password_dict->HasKey("signon_realm")) {
    reply.SendError("Password must include signon_realm.");
    return;
  }
  webkit_glue::PasswordForm to_remove =
      GetPasswordFormFromDict(*password_dict);

  Profile* profile = browser->profile();
  // Use EXPLICIT_ACCESS since passwords can be removed in incognito mode.
  PasswordStore* password_store =
      profile->GetPasswordStore(Profile::EXPLICIT_ACCESS);

  password_store->RemoveLogin(to_remove);
  reply.SendSuccess(NULL);
}

// Sample json input: { "command": "GetSavedPasswords" }
// Refer to GetSavedPasswords() in chrome/test/pyautolib/pyauto.py for sample
// json output.
void TestingAutomationProvider::GetSavedPasswords(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  Profile* profile = browser->profile();
  // Use EXPLICIT_ACCESS since saved passwords can be retrieved in
  // incognito mode.
  PasswordStore* password_store =
      profile->GetPasswordStore(Profile::EXPLICIT_ACCESS);
  password_store->GetAutofillableLogins(
      new AutomationProviderGetPasswordsObserver(this, reply_message));
  // Observer deletes itself after returning.
}

// Refer to ClearBrowsingData() in chrome/test/pyautolib/pyauto.py for sample
// json input.
// Sample json output: {}
void TestingAutomationProvider::ClearBrowsingData(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  std::map<std::string, BrowsingDataRemover::TimePeriod> string_to_time_period;
  string_to_time_period["LAST_HOUR"] = BrowsingDataRemover::LAST_HOUR;
  string_to_time_period["LAST_DAY"] = BrowsingDataRemover::LAST_DAY;
  string_to_time_period["LAST_WEEK"] = BrowsingDataRemover::LAST_WEEK;
  string_to_time_period["FOUR_WEEKS"] = BrowsingDataRemover::FOUR_WEEKS;
  string_to_time_period["EVERYTHING"] = BrowsingDataRemover::EVERYTHING;

  std::map<std::string, int> string_to_mask_value;
  string_to_mask_value["HISTORY"] = BrowsingDataRemover::REMOVE_HISTORY;
  string_to_mask_value["DOWNLOADS"] = BrowsingDataRemover::REMOVE_DOWNLOADS;
  string_to_mask_value["COOKIES"] = BrowsingDataRemover::REMOVE_COOKIES;
  string_to_mask_value["PASSWORDS"] = BrowsingDataRemover::REMOVE_PASSWORDS;
  string_to_mask_value["FORM_DATA"] = BrowsingDataRemover::REMOVE_FORM_DATA;
  string_to_mask_value["CACHE"] = BrowsingDataRemover::REMOVE_CACHE;

  std::string time_period;
  ListValue* to_remove;
  if (!args->GetString("time_period", &time_period) ||
      !args->GetList("to_remove", &to_remove)) {
    AutomationJSONReply(this, reply_message)
        .SendError("time_period must be a string and to_remove a list.");
    return;
  }

  int remove_mask = 0;
  int num_removals = to_remove->GetSize();
  for (int i = 0; i < num_removals; i++) {
    std::string removal;
    to_remove->GetString(i, &removal);
    // If the provided string is not part of the map, then error out.
    if (!ContainsKey(string_to_mask_value, removal)) {
      AutomationJSONReply(this, reply_message)
          .SendError("Invalid browsing data string found in to_remove.");
      return;
    }
    remove_mask |= string_to_mask_value[removal];
  }

  if (!ContainsKey(string_to_time_period, time_period)) {
    AutomationJSONReply(this, reply_message)
        .SendError("Invalid string for time_period.");
    return;
  }

  BrowsingDataRemover* remover = new BrowsingDataRemover(
      profile(), string_to_time_period[time_period], base::Time());

  remover->AddObserver(
      new AutomationProviderBrowsingDataObserver(this, reply_message));
  remover->Remove(remove_mask);
  // BrowsingDataRemover deletes itself using DeleteTask.
  // The observer also deletes itself after sending the reply.
}

namespace {

  // Get the TabContents from a dictionary of arguments.
  TabContents* GetTabContentsFromDict(const Browser* browser,
                                      const DictionaryValue* args,
                                      std::string* error_message) {
    int tab_index;
    if (!args->GetInteger("tab_index", &tab_index)) {
      *error_message = "Must include tab_index.";
      return NULL;
    }

    TabContents* tab_contents = browser->GetTabContentsAt(tab_index);
    if (!tab_contents) {
      *error_message = StringPrintf("No tab at index %d.", tab_index);
      return NULL;
    }
    return tab_contents;
  }

  // Get the TranslateInfoBarDelegate from TabContents.
  TranslateInfoBarDelegate* GetTranslateInfoBarDelegate(
      TabContents* tab_contents) {
    for (size_t i = 0; i < tab_contents->infobar_count(); i++) {
      InfoBarDelegate* infobar = tab_contents->GetInfoBarDelegateAt(i);
      if (infobar->AsTranslateInfoBarDelegate())
        return infobar->AsTranslateInfoBarDelegate();
    }
    // No translate infobar.
    return NULL;
  }
}  // namespace

void TestingAutomationProvider::FindInPage(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  std::string error_message;
  TabContents* tab_contents = GetTabContentsFromDict(browser, args,
                                                     &error_message);
  if (!tab_contents) {
    AutomationJSONReply(this, reply_message).SendError(error_message);
    return;
  }
  string16 search_string;
  bool forward;
  bool match_case;
  bool find_next;
  if (!args->GetString("search_string", &search_string)) {
    AutomationJSONReply(this, reply_message).
        SendError("Must include search_string string.");
    return;
  }
  if (!args->GetBoolean("forward", &forward)) {
    AutomationJSONReply(this, reply_message).
        SendError("Must include forward boolean.");
    return;
  }
  if (!args->GetBoolean("match_case", &match_case)) {
    AutomationJSONReply(this, reply_message).
        SendError("Must include match_case boolean.");
    return;
  }
  if (!args->GetBoolean("find_next", &find_next)) {
    AutomationJSONReply(this, reply_message).
        SendError("Must include find_next boolean.");
    return;
  }
  SendFindRequest(tab_contents,
                  true,
                  search_string,
                  forward,
                  match_case,
                  find_next,
                  reply_message);
}

// See GetTranslateInfo() in chrome/test/pyautolib/pyauto.py for sample json
// input and output.
void TestingAutomationProvider::GetTranslateInfo(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  std::string error_message;
  TabContents* tab_contents = GetTabContentsFromDict(browser, args,
                                                     &error_message);
  if (!tab_contents) {
    AutomationJSONReply(this, reply_message).SendError(error_message);
    return;
  }

  // Get the translate bar if there is one and pass it to the observer.
  // The observer will check for null and populate the information accordingly.
  TranslateInfoBarDelegate* translate_bar =
      GetTranslateInfoBarDelegate(tab_contents);

  TabLanguageDeterminedObserver* observer = new TabLanguageDeterminedObserver(
      this, reply_message, tab_contents, translate_bar);
  // If the language for the page hasn't been loaded yet, then just make
  // the observer, otherwise call observe directly.
  TranslateTabHelper* helper = TabContentsWrapper::GetCurrentWrapperForContents(
      tab_contents)->translate_tab_helper();
  std::string language = helper->language_state().original_language();
  if (!language.empty()) {
    observer->Observe(NotificationType::TAB_LANGUAGE_DETERMINED,
                      Source<TabContents>(tab_contents),
                      Details<std::string>(&language));
  }
}

// See SelectTranslateOption() in chrome/test/pyautolib/pyauto.py for sample
// json input.
// Sample json output: {}
void TestingAutomationProvider::SelectTranslateOption(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  std::string option;
  std::string error_message;
  TabContents* tab_contents = GetTabContentsFromDict(browser, args,
                                                     &error_message);
  if (!tab_contents) {
    AutomationJSONReply(this, reply_message).SendError(error_message);
    return;
  }

  TranslateInfoBarDelegate* translate_bar =
      GetTranslateInfoBarDelegate(tab_contents);
  if (!translate_bar) {
    AutomationJSONReply(this, reply_message)
        .SendError("There is no translate bar open.");
    return;
  }

  if (!args->GetString("option", &option)) {
    AutomationJSONReply(this, reply_message).SendError("Must include option");
    return;
  }

  if (option == "translate_page") {
    // Make a new notification observer which will send the reply.
    new PageTranslatedObserver(this, reply_message, tab_contents);
    translate_bar->Translate();
    return;
  } else if (option == "set_target_language") {
    string16 target_language;
    if (!args->GetString("target_language", &target_language)) {
       AutomationJSONReply(this, reply_message)
           .SendError("Must include target_language string.");
      return;
    }
    // Get the target language index based off of the language name.
    size_t target_language_index = TranslateInfoBarDelegate::kNoIndex;
    for (size_t i = 0; i < translate_bar->GetLanguageCount(); i++) {
      if (translate_bar->GetLanguageDisplayableNameAt(i) == target_language) {
        target_language_index = i;
        break;
      }
    }
    if (target_language_index == TranslateInfoBarDelegate::kNoIndex) {
       AutomationJSONReply(this, reply_message)
           .SendError("Invalid target language string.");
       return;
    }
    // If the page has already been translated it will be translated again to
    // the new language. The observer will wait until the page has been
    // translated to reply.
    if (translate_bar->type() == TranslateInfoBarDelegate::AFTER_TRANSLATE) {
      new PageTranslatedObserver(this, reply_message, tab_contents);
      translate_bar->SetTargetLanguage(target_language_index);
      return;
    }
    // Otherwise just send the reply back immediately.
    translate_bar->SetTargetLanguage(target_language_index);
    scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    return_value->SetBoolean("translation_success", true);
    AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
    return;
  } else if (option == "click_always_translate_lang_button") {
    if (!translate_bar->ShouldShowAlwaysTranslateButton()) {
      AutomationJSONReply(this, reply_message)
          .SendError("Always translate button not showing.");
      return;
    }
    // Clicking 'Always Translate' triggers a translation. The observer will
    // wait until the translation is complete before sending the reply.
    new PageTranslatedObserver(this, reply_message, tab_contents);
    translate_bar->AlwaysTranslatePageLanguage();
    return;
  }

  AutomationJSONReply reply(this, reply_message);
  if (option == "never_translate_language") {
    if (translate_bar->IsLanguageBlacklisted()) {
      reply.SendError("The language was already blacklisted.");
      return;
    }
    translate_bar->ToggleLanguageBlacklist();
    reply.SendSuccess(NULL);
  } else if (option == "never_translate_site") {
    if (translate_bar->IsSiteBlacklisted()) {
      reply.SendError("The site was already blacklisted.");
      return;
    }
    translate_bar->ToggleSiteBlacklist();
    reply.SendSuccess(NULL);
  } else if (option == "toggle_always_translate") {
    translate_bar->ToggleAlwaysTranslate();
    reply.SendSuccess(NULL);
  } else if (option == "revert_translation") {
    translate_bar->RevertTranslation();
    reply.SendSuccess(NULL);
  } else if (option == "click_never_translate_lang_button") {
    if (!translate_bar->ShouldShowNeverTranslateButton()) {
      reply.SendError("Always translate button not showing.");
      return;
    }
    translate_bar->NeverTranslatePageLanguage();
    reply.SendSuccess(NULL);
  } else if (option == "decline_translation") {
    // This is the function called when an infobar is dismissed or when the
    // user clicks the 'Nope' translate button.
    translate_bar->TranslationDeclined();
    tab_contents->RemoveInfoBar(translate_bar);
    reply.SendSuccess(NULL);
  } else {
    reply.SendError("Invalid string found for option.");
  }
}

// Sample json input: { "command": "GetBlockedPopupsInfo",
//                      "tab_index": 1 }
// Refer GetBlockedPopupsInfo() in pyauto.py for sample output.
void TestingAutomationProvider::GetBlockedPopupsInfo(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  std::string error_message;
  TabContents* tab_contents = GetTabContentsFromDict(
      browser, args, &error_message);
  if (!tab_contents) {
    reply.SendError(error_message);
    return;
  }
  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
  BlockedContentContainer* popup_container =
      tab_contents->blocked_content_container();
  ListValue* blocked_popups_list = new ListValue;
  if (popup_container) {
    std::vector<TabContents*> blocked_contents;
    popup_container->GetBlockedContents(&blocked_contents);
    for (std::vector<TabContents*>::const_iterator it =
             blocked_contents.begin(); it != blocked_contents.end(); ++it) {
      DictionaryValue* item = new DictionaryValue;
      item->SetString("url", (*it)->GetURL().spec());
      item->SetString("title", (*it)->GetTitle());
      blocked_popups_list->Append(item);
    }
  }
  return_value->Set("blocked_popups", blocked_popups_list);
  reply.SendSuccess(return_value.get());
}

// Refer UnblockAndLaunchBlockedPopup() in pyauto.py for sample input.
void TestingAutomationProvider::UnblockAndLaunchBlockedPopup(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  std::string error_message;
  TabContents* tab_contents = GetTabContentsFromDict(
      browser, args, &error_message);
  if (!tab_contents) {
    reply.SendError(error_message);
    return;
  }
  int popup_index;
  if (!args->GetInteger("popup_index", &popup_index)) {
    reply.SendError("Need popup_index arg");
    return;
  }
  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
  BlockedContentContainer* content_container =
      tab_contents->blocked_content_container();
  if (!content_container ||
      popup_index >= (int)content_container->GetBlockedContentsCount()) {
    reply.SendError(StringPrintf("No popup at index %d", popup_index));
    return;
  }
  std::vector<TabContents*> blocked_contents;
  content_container->GetBlockedContents(&blocked_contents);
  content_container->LaunchForContents(blocked_contents[popup_index]);
  reply.SendSuccess(NULL);
}

// Sample json input: { "command": "GetThemeInfo" }
// Refer GetThemeInfo() in chrome/test/pyautolib/pyauto.py for sample output.
void TestingAutomationProvider::GetThemeInfo(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
  const Extension* theme = ThemeServiceFactory::GetThemeForProfile(profile());
  if (theme) {
    return_value->SetString("name", theme->name());
    return_value->Set("images", theme->GetThemeImages()->DeepCopy());
    return_value->Set("colors", theme->GetThemeColors()->DeepCopy());
    return_value->Set("tints", theme->GetThemeTints()->DeepCopy());
  }
  AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
}

namespace {

ListValue* GetHostPermissions(const Extension* ext) {
  ListValue* permissions = new ListValue;
  const URLPatternList pattern_list =
      ext->GetEffectiveHostPermissions().patterns();
  for (URLPatternList::const_iterator perm = pattern_list.begin();
       perm != pattern_list.end(); ++perm) {
    permissions->Append(new StringValue(perm->GetAsString()));
  }
  return permissions;
}

ListValue* GetAPIPermissions(const Extension* ext) {
  ListValue* permissions = new ListValue;
  std::set<std::string> perm_list = ext->api_permissions();
  for (std::set<std::string>::const_iterator perm = perm_list.begin();
       perm != perm_list.end(); ++perm) {
    permissions->Append(new StringValue(perm->c_str()));
  }
  return permissions;
}

}  // namespace

// Sample json input: { "command": "GetExtensionsInfo" }
// See GetExtensionsInfo() in chrome/test/pyautolib/pyauto.py for sample json
// output.
void TestingAutomationProvider::GetExtensionsInfo(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  ExtensionService* service = profile()->GetExtensionService();
  if (!service) {
    reply.SendError("No extensions service.");
  }
  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
  ListValue* extensions_values = new ListValue;
  const ExtensionList* extensions = service->extensions();
  for (ExtensionList::const_iterator it = extensions->begin();
       it != extensions->end(); ++it) {
    const Extension* extension = *it;
    DictionaryValue* extension_value = new DictionaryValue;
    extension_value->SetString("id", extension->id());
    extension_value->SetString("version", extension->VersionString());
    extension_value->SetString("name", extension->name());
    extension_value->SetString("public_key", extension->public_key());
    extension_value->SetString("description", extension->description());
    extension_value->SetString("background_url",
                               extension->background_url().spec());
    extension_value->SetString("options_url",
                               extension->options_url().spec());
    extension_value->Set("host_permissions", GetHostPermissions(extension));
    extension_value->Set("api_permissions", GetAPIPermissions(extension));
    extensions_values->Append(extension_value);
  }
  return_value->Set("extensions", extensions_values);
  reply.SendSuccess(return_value.get());
}

// See UninstallExtensionById() in chrome/test/pyautolib/pyauto.py for sample
// json input.
// Sample json output: {}
void TestingAutomationProvider::UninstallExtensionById(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  std::string id;
  if (!args->GetString("id", &id)) {
    AutomationJSONReply(this, reply_message).SendError(
        "Must include string id.");
    return;
  }
  ExtensionService* service = profile()->GetExtensionService();
  if (!service) {
    AutomationJSONReply(this, reply_message).SendError(
        "No extensions service.");
    return;
  }

  if (!service->GetExtensionById(id, true) &&
      !service->GetTerminatedExtension(id)) {
    // The extension ID does not correspond to any extension, whether crashed
    // or not.
    AutomationJSONReply(this, reply_message).SendError(base::StringPrintf(
        "Extension does not exist: %s.", id.c_str()));
    return;
  }

  // Wait for a notification indicating that the extension with the given ID
  // has been uninstalled.  This observer will delete itself.
  new ExtensionUninstallObserver(this, reply_message, id);
  service->UninstallExtension(id, false, NULL);
}

// Sample json input:
//    { "command": "GetAutofillProfile" }
// Refer to GetAutofillProfile() in chrome/test/pyautolib/pyauto.py for sample
// json output.
void TestingAutomationProvider::GetAutofillProfile(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  // Get the AutofillProfiles currently in the database.
  int tab_index = 0;
  if (!args->GetInteger("tab_index", &tab_index)) {
    reply.SendError("Invalid or missing tab_index integer value.");
    return;
  }

  TabContents* tab_contents = browser->GetTabContentsAt(tab_index);
  if (tab_contents) {
    PersonalDataManager* pdm = tab_contents->profile()->GetOriginalProfile()
        ->GetPersonalDataManager();
    if (pdm) {
      std::vector<AutofillProfile*> autofill_profiles = pdm->profiles();
      std::vector<CreditCard*> credit_cards = pdm->credit_cards();

      ListValue* profiles = GetListFromAutofillProfiles(autofill_profiles);
      ListValue* cards = GetListFromCreditCards(credit_cards);

      scoped_ptr<DictionaryValue> return_value(new DictionaryValue);

      return_value->Set("profiles", profiles);
      return_value->Set("credit_cards", cards);
      reply.SendSuccess(return_value.get());
    } else {
      reply.SendError("No PersonalDataManager.");
      return;
    }
  } else {
    reply.SendError("No tab at that index.");
    return;
  }
}

// Refer to FillAutofillProfile() in chrome/test/pyautolib/pyauto.py for sample
// json input.
// Sample json output: {}
void TestingAutomationProvider::FillAutofillProfile(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  ListValue* profiles = NULL;
  ListValue* cards = NULL;

  // It's ok for profiles/credit_cards elements to be missing.
  args->GetList("profiles", &profiles);
  args->GetList("credit_cards", &cards);

  std::string error_mesg;

  std::vector<AutofillProfile> autofill_profiles;
  std::vector<CreditCard> credit_cards;
  // Create an AutofillProfile for each of the dictionary profiles.
  if (profiles) {
    autofill_profiles = GetAutofillProfilesFromList(*profiles, &error_mesg);
  }
  // Create a CreditCard for each of the dictionary values.
  if (cards) {
    credit_cards = GetCreditCardsFromList(*cards, &error_mesg);
  }
  if (!error_mesg.empty()) {
    reply.SendError(error_mesg);
    return;
  }

  // Save the AutofillProfiles.
  int tab_index = 0;
  if (!args->GetInteger("tab_index", &tab_index)) {
    reply.SendError("Invalid or missing tab_index integer");
    return;
  }

  TabContents* tab_contents = browser->GetTabContentsAt(tab_index);

  if (tab_contents) {
    PersonalDataManager* pdm = tab_contents->profile()
        ->GetPersonalDataManager();
    if (pdm) {
      if (profiles)
        pdm->SetProfiles(&autofill_profiles);
      if (cards)
        pdm->SetCreditCards(&credit_cards);
    } else {
      reply.SendError("No PersonalDataManager.");
      return;
    }
  } else {
    reply.SendError("No tab at that index.");
    return;
  }
  reply.SendSuccess(NULL);
}

// Sample json output: { "success": true }
void TestingAutomationProvider::SignInToSync(Browser* browser,
                                             DictionaryValue* args,
                                             IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  std::string username;
  std::string password;
  if (!args->GetString("username", &username) ||
      !args->GetString("password", &password)) {
      reply.SendError("Invalid or missing args");
      return;
  }
  if (sync_waiter_.get() == NULL) {
    sync_waiter_.reset(new ProfileSyncServiceHarness(
        browser->profile(), username, password, 0));
  } else {
    sync_waiter_->SetCredentials(username, password);
  }
  if (sync_waiter_->SetupSync()) {
    scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    return_value->SetBoolean("success", true);
    reply.SendSuccess(return_value.get());
  } else {
    reply.SendError("Signing in to sync was unsuccessful");
  }
}

// Sample json output:
// {u'summary': u'SYNC DISABLED'}
//
// { u'authenticated': True,
//   u'last synced': u'Just now',
//   u'summary': u'READY',
//   u'sync url': u'clients4.google.com',
//   u'updates received': 42,
//   u'synced datatypes': [ u'Bookmarks',
//                          u'Preferences',
//                          u'Passwords',
//                          u'Autofill',
//                          u'Themes',
//                          u'Extensions',
//                          u'Apps']}
void TestingAutomationProvider::GetSyncInfo(Browser* browser,
                                            DictionaryValue* args,
                                            IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  DictionaryValue* sync_info = new DictionaryValue;
  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
  if (sync_waiter_.get() == NULL) {
    sync_waiter_.reset(
        ProfileSyncServiceHarness::CreateAndAttach(browser->profile()));
  }
  if (!sync_waiter_->IsSyncAlreadySetup()) {
    sync_info->SetString("summary", "SYNC DISABLED");
  } else {
    ProfileSyncService* service = sync_waiter_->service();
    ProfileSyncService::Status status = sync_waiter_->GetStatus();
    sync_info->SetString("summary",
        ProfileSyncService::BuildSyncStatusSummaryText(status.summary));
    sync_info->SetString("sync url", service->sync_service_url().host());
    sync_info->SetBoolean("authenticated", status.authenticated);
    sync_info->SetString("last synced", service->GetLastSyncedTimeString());
    sync_info->SetInteger("updates received", status.updates_received);
    ListValue* synced_datatype_list = new ListValue;
    syncable::ModelTypeSet synced_datatypes;
    service->GetPreferredDataTypes(&synced_datatypes);
    for (syncable::ModelTypeSet::iterator it = synced_datatypes.begin();
         it != synced_datatypes.end(); ++it) {
      synced_datatype_list->Append(
          new StringValue(syncable::ModelTypeToString(*it)));
    }
    sync_info->Set("synced datatypes", synced_datatype_list);
  }
  return_value->Set("sync_info", sync_info);
  reply.SendSuccess(return_value.get());
}

// Sample json output: { "success": true }
void TestingAutomationProvider::AwaitSyncCycleCompletion(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  if (sync_waiter_.get() == NULL) {
    sync_waiter_.reset(
        ProfileSyncServiceHarness::CreateAndAttach(browser->profile()));
  }
  if (!sync_waiter_->IsSyncAlreadySetup()) {
    reply.SendError("Not signed in to sync");
    return;
  }
  // Ensure that the profile sync service is initialized before waiting for sync
  // to complete. In cases where the browser is restarted with sync enabled,
  // the sync service may take a while to get reinitialized.
  if (!browser->profile()->GetProfileSyncService()) {
    reply.SendError("ProfileSyncService not initialized.");
    return;
  }
  sync_waiter_->AwaitSyncCycleCompletion("Waiting for sync cycle");
  ProfileSyncService::Status status = sync_waiter_->GetStatus();
  if (status.summary == ProfileSyncService::Status::READY) {
    scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    return_value->SetBoolean("success", true);
    reply.SendSuccess(return_value.get());
  } else {
    std::string error_msg = "Wait for sync cycle was unsuccessful. "
                            "Sync status: ";
    error_msg.append(
        ProfileSyncService::BuildSyncStatusSummaryText(status.summary));
    reply.SendError(error_msg);
  }
}

// Refer to EnableSyncForDatatypes() in chrome/test/pyautolib/pyauto.py for
// sample json input. Sample json output: { "success": true }
void TestingAutomationProvider::EnableSyncForDatatypes(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  if (sync_waiter_.get() == NULL) {
    sync_waiter_.reset(
        ProfileSyncServiceHarness::CreateAndAttach(browser->profile()));
  }
  if (!sync_waiter_->IsSyncAlreadySetup()) {
    reply.SendError("Not signed in to sync");
    return;
  }
  ListValue* datatypes = NULL;
  if (!args->GetList("datatypes", &datatypes)) {
    reply.SendError("Invalid or missing args");
    return;
  }
  std::string first_datatype;
  datatypes->GetString(0, &first_datatype);
  if (first_datatype == "All") {
    sync_waiter_->EnableSyncForAllDatatypes();
  } else {
    int num_datatypes = datatypes->GetSize();
    for (int i = 0; i < num_datatypes; ++i) {
      std::string datatype_string;
      datatypes->GetString(i, &datatype_string);
      syncable::ModelType datatype =
          syncable::ModelTypeFromString(datatype_string);
      if (datatype == syncable::UNSPECIFIED) {
        AutomationJSONReply(this, reply_message).SendError(StringPrintf(
            "Invalid datatype string: %s.", datatype_string.c_str()));
        return;
      }
      sync_waiter_->EnableSyncForDatatype(datatype);
      sync_waiter_->AwaitSyncCycleCompletion(StringPrintf(
          "Enabling datatype: %s", datatype_string.c_str()));
    }
  }
  ProfileSyncService::Status status = sync_waiter_->GetStatus();
  if (status.summary == ProfileSyncService::Status::READY ||
      status.summary == ProfileSyncService::Status::SYNCING) {
    scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    return_value->SetBoolean("success", true);
    reply.SendSuccess(return_value.get());
  } else {
    reply.SendError("Enabling sync for given datatypes was unsuccessful");
  }
}

// Refer to DisableSyncForDatatypes() in chrome/test/pyautolib/pyauto.py for
// sample json input. Sample json output: { "success": true }
void TestingAutomationProvider::DisableSyncForDatatypes(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  if (sync_waiter_.get() == NULL) {
    sync_waiter_.reset(
        ProfileSyncServiceHarness::CreateAndAttach(browser->profile()));
  }
  if (!sync_waiter_->IsSyncAlreadySetup()) {
    reply.SendError("Not signed in to sync");
    return;
  }
  ListValue* datatypes = NULL;
  if (!args->GetList("datatypes", &datatypes)) {
    reply.SendError("Invalid or missing args");
    return;
  }
  std::string first_datatype;
  if (!datatypes->GetString(0, &first_datatype)) {
    reply.SendError("Invalid or missing string");
    return;
  }
  if (first_datatype == "All") {
    sync_waiter_->DisableSyncForAllDatatypes();
    ProfileSyncService::Status status = sync_waiter_->GetStatus();
    if (status.summary != ProfileSyncService::Status::READY &&
        status.summary != ProfileSyncService::Status::SYNCING) {
      scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
      return_value->SetBoolean("success", true);
      reply.SendSuccess(return_value.get());
    } else {
      reply.SendError("Disabling all sync datatypes was unsuccessful");
    }
  } else {
    int num_datatypes = datatypes->GetSize();
    for (int i = 0; i < num_datatypes; i++) {
      std::string datatype_string;
      datatypes->GetString(i, &datatype_string);
      syncable::ModelType datatype =
          syncable::ModelTypeFromString(datatype_string);
      if (datatype == syncable::UNSPECIFIED) {
        AutomationJSONReply(this, reply_message).SendError(StringPrintf(
            "Invalid datatype string: %s.", datatype_string.c_str()));
        return;
      }
      sync_waiter_->DisableSyncForDatatype(datatype);
      sync_waiter_->AwaitSyncCycleCompletion(StringPrintf(
          "Disabling datatype: %s", datatype_string.c_str()));
    }
    ProfileSyncService::Status status = sync_waiter_->GetStatus();
    if (status.summary == ProfileSyncService::Status::READY ||
        status.summary == ProfileSyncService::Status::SYNCING) {
      scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
      return_value->SetBoolean("success", true);
      reply.SendSuccess(return_value.get());
    } else {
      reply.SendError("Disabling sync for given datatypes was unsuccessful");
    }
  }
}

/* static */
ListValue* TestingAutomationProvider::GetListFromAutofillProfiles(
    const std::vector<AutofillProfile*>& autofill_profiles) {
  ListValue* profiles = new ListValue;

  std::map<AutofillFieldType, std::string> autofill_type_to_string
      = GetAutofillFieldToStringMap();

  // For each AutofillProfile, transform it to a dictionary object to return.
  for (std::vector<AutofillProfile*>::const_iterator it =
           autofill_profiles.begin();
       it != autofill_profiles.end(); ++it) {
    AutofillProfile* profile = *it;
    DictionaryValue* profile_info = new DictionaryValue;
    // For each of the types, if it has a value, add it to the dictionary.
    for (std::map<AutofillFieldType, std::string>::iterator
         type_it = autofill_type_to_string.begin();
         type_it != autofill_type_to_string.end(); ++type_it) {
      string16 value = profile->GetInfo(type_it->first);
      if (value.length()) {  // If there was something stored for that value.
        profile_info->SetString(type_it->second, value);
      }
    }
    profiles->Append(profile_info);
  }
  return profiles;
}

/* static */
ListValue* TestingAutomationProvider::GetListFromCreditCards(
    const std::vector<CreditCard*>& credit_cards) {
  ListValue* cards = new ListValue;

  std::map<AutofillFieldType, std::string> credit_card_type_to_string =
      GetCreditCardFieldToStringMap();

  // For each AutofillProfile, transform it to a dictionary object to return.
  for (std::vector<CreditCard*>::const_iterator it =
           credit_cards.begin();
       it != credit_cards.end(); ++it) {
    CreditCard* card = *it;
    DictionaryValue* card_info = new DictionaryValue;
    // For each of the types, if it has a value, add it to the dictionary.
    for (std::map<AutofillFieldType, std::string>::iterator type_it =
        credit_card_type_to_string.begin();
        type_it != credit_card_type_to_string.end(); ++type_it) {
      string16 value = card->GetInfo(type_it->first);
      // If there was something stored for that value.
      if (value.length()) {
        card_info->SetString(type_it->second, value);
      }
    }
    cards->Append(card_info);
  }
  return cards;
}

/* static */
std::vector<AutofillProfile>
TestingAutomationProvider::GetAutofillProfilesFromList(
    const ListValue& profiles, std::string* error_message) {
  std::vector<AutofillProfile> autofill_profiles;
  DictionaryValue* profile_info = NULL;
  string16 current_value;

  std::map<AutofillFieldType, std::string> autofill_type_to_string =
      GetAutofillFieldToStringMap();

  int num_profiles = profiles.GetSize();
  for (int i = 0; i < num_profiles; i++) {
    profiles.GetDictionary(i, &profile_info);
    AutofillProfile profile;
    // Loop through the possible profile types and add those provided.
    for (std::map<AutofillFieldType, std::string>::iterator type_it =
         autofill_type_to_string.begin();
         type_it != autofill_type_to_string.end(); ++type_it) {
      if (profile_info->HasKey(type_it->second)) {
        if (profile_info->GetString(type_it->second,
                                    &current_value)) {
          profile.SetInfo(type_it->first, current_value);
        } else {
          *error_message= "All values must be strings";
          break;
        }
      }
    }
    autofill_profiles.push_back(profile);
  }
  return autofill_profiles;
}

/* static */
std::vector<CreditCard> TestingAutomationProvider::GetCreditCardsFromList(
    const ListValue& cards, std::string* error_message) {
  std::vector<CreditCard> credit_cards;
  DictionaryValue* card_info = NULL;
  string16 current_value;

  std::map<AutofillFieldType, std::string> credit_card_type_to_string =
      GetCreditCardFieldToStringMap();

  int num_credit_cards = cards.GetSize();
  for (int i = 0; i < num_credit_cards; i++) {
    cards.GetDictionary(i, &card_info);
    CreditCard card;
    // Loop through the possible credit card fields and add those provided.
    for (std::map<AutofillFieldType, std::string>::iterator type_it =
        credit_card_type_to_string.begin();
        type_it != credit_card_type_to_string.end(); ++type_it) {
      if (card_info->HasKey(type_it->second)) {
        if (card_info->GetString(type_it->second, &current_value)) {
          card.SetInfo(type_it->first, current_value);
        } else {
          *error_message= "All values must be strings";
          break;
        }
      }
    }
    credit_cards.push_back(card);
  }
  return credit_cards;
}

/* static */
std::map<AutofillFieldType, std::string>
    TestingAutomationProvider::GetAutofillFieldToStringMap() {
  std::map<AutofillFieldType, std::string> autofill_type_to_string;
  // Strings ordered by order of fields when adding a profile in Autofill prefs.
  autofill_type_to_string[NAME_FIRST] = "NAME_FIRST";
  autofill_type_to_string[NAME_MIDDLE] = "NAME_MIDDLE";
  autofill_type_to_string[NAME_LAST] = "NAME_LAST";
  autofill_type_to_string[COMPANY_NAME] = "COMPANY_NAME";
  autofill_type_to_string[EMAIL_ADDRESS] = "EMAIL_ADDRESS";
  autofill_type_to_string[ADDRESS_HOME_LINE1] = "ADDRESS_HOME_LINE1";
  autofill_type_to_string[ADDRESS_HOME_LINE2] = "ADDRESS_HOME_LINE2";
  autofill_type_to_string[ADDRESS_HOME_CITY] = "ADDRESS_HOME_CITY";
  autofill_type_to_string[ADDRESS_HOME_STATE] = "ADDRESS_HOME_STATE";
  autofill_type_to_string[ADDRESS_HOME_ZIP] = "ADDRESS_HOME_ZIP";
  autofill_type_to_string[ADDRESS_HOME_COUNTRY] = "ADDRESS_HOME_COUNTRY";
  autofill_type_to_string[PHONE_HOME_COUNTRY_CODE] =
      "PHONE_HOME_COUNTRY_CODE";
  autofill_type_to_string[PHONE_HOME_CITY_CODE] = "PHONE_HOME_CITY_CODE";
  autofill_type_to_string[PHONE_HOME_WHOLE_NUMBER] =
      "PHONE_HOME_WHOLE_NUMBER";
  autofill_type_to_string[PHONE_FAX_COUNTRY_CODE] = "PHONE_FAX_COUNTRY_CODE";
  autofill_type_to_string[PHONE_FAX_CITY_CODE] = "PHONE_FAX_CITY_CODE";
  autofill_type_to_string[PHONE_FAX_WHOLE_NUMBER] = "PHONE_FAX_WHOLE_NUMBER";
  return autofill_type_to_string;
}

/* static */
std::map<AutofillFieldType, std::string>
    TestingAutomationProvider::GetCreditCardFieldToStringMap() {
  std::map<AutofillFieldType, std::string> credit_card_type_to_string;
  credit_card_type_to_string[CREDIT_CARD_NAME] = "CREDIT_CARD_NAME";
  credit_card_type_to_string[CREDIT_CARD_NUMBER] = "CREDIT_CARD_NUMBER";
  credit_card_type_to_string[CREDIT_CARD_EXP_MONTH] = "CREDIT_CARD_EXP_MONTH";
  credit_card_type_to_string[CREDIT_CARD_EXP_4_DIGIT_YEAR] =
      "CREDIT_CARD_EXP_4_DIGIT_YEAR";
  return credit_card_type_to_string;
}

// Refer to GetActiveNotifications() in chrome/test/pyautolib/pyauto.py for
// sample json input/output.
void TestingAutomationProvider::GetActiveNotifications(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  new GetActiveNotificationsObserver(this, reply_message);
}

// Refer to CloseNotification() in chrome/test/pyautolib/pyauto.py for
// sample json input.
// Returns empty json message.
void TestingAutomationProvider::CloseNotification(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  int index;
  if (!args->GetInteger("index", &index)) {
    AutomationJSONReply(this, reply_message)
        .SendError("'index' missing or invalid.");
    return;
  }
  NotificationUIManager* manager = g_browser_process->notification_ui_manager();
  BalloonCollection* collection = manager->balloon_collection();
  const BalloonCollection::Balloons& balloons = collection->GetActiveBalloons();
  int balloon_count = static_cast<int>(balloons.size());
  if (index < 0 || index >= balloon_count) {
    AutomationJSONReply(this, reply_message)
        .SendError(StringPrintf("No notification at index %d", index));
    return;
  }
  // This will delete itself when finished.
  new OnNotificationBalloonCountObserver(
      this, reply_message, collection, balloon_count - 1);
  manager->CancelById(balloons[index]->notification().notification_id());
}

// Refer to WaitForNotificationCount() in chrome/test/pyautolib/pyauto.py for
// sample json input.
// Returns empty json message.
void TestingAutomationProvider::WaitForNotificationCount(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  int count;
  if (!args->GetInteger("count", &count)) {
    AutomationJSONReply(this, reply_message)
        .SendError("'count' missing or invalid.");
    return;
  }
  NotificationUIManager* manager = g_browser_process->notification_ui_manager();
  BalloonCollection* collection = manager->balloon_collection();
  const BalloonCollection::Balloons& balloons = collection->GetActiveBalloons();
  if (static_cast<int>(balloons.size()) == count) {
    AutomationJSONReply(this, reply_message).SendSuccess(NULL);
    return;
  }
  // This will delete itself when finished.
  new OnNotificationBalloonCountObserver(
      this, reply_message, collection, count);
}

// Sample JSON input: { "command": "GetNTPInfo" }
// For output, refer to chrome/test/pyautolib/ntp_model.py.
void TestingAutomationProvider::GetNTPInfo(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  // This observer will delete itself.
  new NTPInfoObserver(this, reply_message, &consumer_);
}

void TestingAutomationProvider::MoveNTPMostVisitedThumbnail(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  std::string url, error;
  int index, old_index;
  if (!args->GetString("url", &url)) {
    reply.SendError("Missing or invalid 'url' key.");
    return;
  }
  if (!args->GetInteger("index", &index)) {
    reply.SendError("Missing or invalid 'index' key.");
    return;
  }
  if (!args->GetInteger("old_index", &old_index)) {
    reply.SendError("Missing or invalid 'old_index' key");
    return;
  }
  history::TopSites* top_sites = browser->profile()->GetTopSites();
  if (!top_sites) {
    reply.SendError("TopSites service is not initialized.");
    return;
  }
  GURL swapped;
  // If thumbnail A is switching positions with a pinned thumbnail B, B
  // should be pinned in the old index of A.
  if (top_sites->GetPinnedURLAtIndex(index, &swapped)) {
    top_sites->AddPinnedURL(swapped, old_index);
  }
  top_sites->AddPinnedURL(GURL(url), index);
  reply.SendSuccess(NULL);
}

void TestingAutomationProvider::RemoveNTPMostVisitedThumbnail(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  std::string url;
  if (!args->GetString("url", &url)) {
    reply.SendError("Missing or invalid 'url' key.");
    return;
  }
  history::TopSites* top_sites = browser->profile()->GetTopSites();
  if (!top_sites) {
    reply.SendError("TopSites service is not initialized.");
    return;
  }
  top_sites->AddBlacklistedURL(GURL(url));
  reply.SendSuccess(NULL);
}

void TestingAutomationProvider::UnpinNTPMostVisitedThumbnail(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  std::string url;
  if (!args->GetString("url", &url)) {
    reply.SendError("Missing or invalid 'url' key.");
    return;
  }
  history::TopSites* top_sites = browser->profile()->GetTopSites();
  if (!top_sites) {
    reply.SendError("TopSites service is not initialized.");
    return;
  }
  top_sites->RemovePinnedURL(GURL(url));
  reply.SendSuccess(NULL);
}

void TestingAutomationProvider::RestoreAllNTPMostVisitedThumbnails(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  history::TopSites* top_sites = browser->profile()->GetTopSites();
  if (!top_sites) {
    reply.SendError("TopSites service is not initialized.");
    return;
  }
  top_sites->ClearBlacklistedURLs();
  reply.SendSuccess(NULL);
}

void TestingAutomationProvider::KillRendererProcess(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  int pid;
  if (!args->GetInteger("pid", &pid)) {
    AutomationJSONReply(this, reply_message)
        .SendError("'pid' key missing or invalid.");
    return;
  }
  base::ProcessHandle process;
  if (!base::OpenProcessHandle(static_cast<base::ProcessId>(pid), &process)) {
    AutomationJSONReply(this, reply_message).SendError(base::StringPrintf(
        "Failed to open process handle for pid %d", pid));
    return;
  }
  new RendererProcessClosedObserver(this, reply_message);
  base::KillProcess(process, 0, false);
  base::CloseProcessHandle(process);
}

bool TestingAutomationProvider::BuildWebKeyEventFromArgs(
    DictionaryValue* args,
    std::string* error,
    NativeWebKeyboardEvent* event) {
  int type, modifiers;
  bool is_system_key;
  string16 unmodified_text, text;
  std::string key_identifier;
  if (!args->GetInteger("type", &type)) {
    *error = "'type' missing or invalid.";
    return false;
  }
  if (!args->GetBoolean("isSystemKey", &is_system_key)) {
    *error = "'isSystemKey' missing or invalid.";
    return false;
  }
  if (!args->GetString("unmodifiedText", &unmodified_text)) {
    *error = "'unmodifiedText' missing or invalid.";
    return false;
  }
  if (!args->GetString("text", &text)) {
    *error = "'text' missing or invalid.";
    return false;
  }
  if (!args->GetInteger("nativeKeyCode", &event->nativeKeyCode)) {
    *error = "'nativeKeyCode' missing or invalid.";
    return false;
  }
  if (!args->GetInteger("windowsKeyCode", &event->windowsKeyCode)) {
    *error = "'windowsKeyCode' missing or invalid.";
    return false;
  }
  if (!args->GetInteger("modifiers", &modifiers)) {
    *error = "'modifiers' missing or invalid.";
    return false;
  }
  if (args->GetString("keyIdentifier", &key_identifier)) {
    base::strlcpy(event->keyIdentifier,
                  key_identifier.c_str(),
                  WebKit::WebKeyboardEvent::keyIdentifierLengthCap);
  } else {
    event->setKeyIdentifierFromWindowsKeyCode();
  }

  if (type == automation::kRawKeyDownType) {
    event->type = WebKit::WebInputEvent::RawKeyDown;
  } else if (type == automation::kKeyDownType) {
    event->type = WebKit::WebInputEvent::KeyDown;
  } else if (type == automation::kKeyUpType) {
    event->type = WebKit::WebInputEvent::KeyUp;
  } else if (type == automation::kCharType) {
    event->type = WebKit::WebInputEvent::Char;
  } else {
    *error = "'type' refers to an unrecognized keyboard event type";
    return false;
  }

  string16 unmodified_text_truncated = unmodified_text.substr(
      0, WebKit::WebKeyboardEvent::textLengthCap - 1);
  memcpy(event->unmodifiedText,
         unmodified_text_truncated.c_str(),
         unmodified_text_truncated.length() + 1);
  string16 text_truncated = text.substr(
      0, WebKit::WebKeyboardEvent::textLengthCap - 1);
  memcpy(event->text, text_truncated.c_str(), text_truncated.length() + 1);

  event->modifiers = 0;
  if (modifiers & automation::kShiftKeyMask)
    event->modifiers |= WebKit::WebInputEvent::ShiftKey;
  if (modifiers & automation::kControlKeyMask)
    event->modifiers |= WebKit::WebInputEvent::ControlKey;
  if (modifiers & automation::kAltKeyMask)
    event->modifiers |= WebKit::WebInputEvent::AltKey;
  if (modifiers & automation::kMetaKeyMask)
    event->modifiers |= WebKit::WebInputEvent::MetaKey;

  event->isSystemKey = is_system_key;
  event->timeStampSeconds = base::Time::Now().ToDoubleT();
  event->skip_in_browser = true;
  return true;
}

void TestingAutomationProvider::SendWebkitKeyEvent(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  NativeWebKeyboardEvent event;
  // BuildWebKeyEventFromArgs handles telling what when wrong and sending
  // the reply message, if it failed we just have to stop here.
  std::string error;
  if (!BuildWebKeyEventFromArgs(args, &error, &event)) {
    AutomationJSONReply(this, reply_message).SendError(error);
    return;
  }

  TabContents* tab_contents;
  if (!GetTabFromJSONArgs(args, &tab_contents, &error)) {
    AutomationJSONReply(this, reply_message).SendError(error);
    return;
  }
  new InputEventAckNotificationObserver(this, reply_message, event.type);
  tab_contents->render_view_host()->ForwardKeyboardEvent(event);
}

void TestingAutomationProvider::SendOSLevelKeyEventToTab(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  int modifiers, keycode;
  if (!args->GetInteger("keyCode", &keycode)) {
    AutomationJSONReply(this, reply_message)
        .SendError("'keyCode' missing or invalid.");
    return;
  }
  if (!args->GetInteger("modifiers", &modifiers)) {
    AutomationJSONReply(this, reply_message)
        .SendError("'modifiers' missing or invalid.");
    return;
  }

  std::string error;
  Browser* browser;
  TabContents* tab_contents;
  if (!GetBrowserAndTabFromJSONArgs(args, &browser, &tab_contents, &error)) {
    AutomationJSONReply(this, reply_message).SendError(error);
    return;
  }
  // The key events will be sent to the browser window, we need the current tab
  // containing the element we send the text in to be shown.
  browser->ActivateTabAt(
      browser->GetIndexOfController(&tab_contents->controller()), true);

  BrowserWindow* browser_window = browser->window();
  if (!browser_window) {
    AutomationJSONReply(this, reply_message)
        .SendError("Could not get the browser window");
    return;
  }
  gfx::NativeWindow window = browser_window->GetNativeHandle();
  if (!window) {
    AutomationJSONReply(this, reply_message)
        .SendError("Could not get the browser window handle");
    return;
  }

  bool control = !!(modifiers & automation::kControlKeyMask);
  bool shift = !!(modifiers & automation::kShiftKeyMask);
  bool alt = !!(modifiers & automation::kAltKeyMask);
  bool meta = !!(modifiers & automation::kMetaKeyMask);
  if (!ui_controls::SendKeyPressNotifyWhenDone(
          window, static_cast<ui::KeyboardCode>(keycode),
          control, shift, alt, meta,
          NewRunnableMethod(this,
              &TestingAutomationProvider::SendSuccessReply, reply_message))) {
    AutomationJSONReply(this, reply_message)
        .SendError("Could not send the native key event");
  }
}

void TestingAutomationProvider::SendSuccessReply(IPC::Message* reply_message) {
  AutomationJSONReply(this, reply_message).SendSuccess(NULL);
}

// Sample JSON input: { "command": "GetNTPThumbnailMode" }
// For output, refer to GetNTPThumbnailMode() in
// chrome/test/pyautolib/pyauto.py.
void TestingAutomationProvider::GetNTPThumbnailMode(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  const int shown_sections = ShownSectionsHandler::GetShownSections(
      browser->profile()->GetPrefs());

  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
  return_value->SetBoolean("apps", shown_sections & APPS ? true : false);
  return_value->SetBoolean("most_visited",
                           shown_sections & THUMB ? true : false);

  AutomationJSONReply reply(this, reply_message);
  reply.SendSuccess(return_value.get());
}

// Sample JSON input: { "command": "SetNTPThumbnailMode", "section": "apps",
//                      "turn_on": true }
// Refer to SetNTPThumbnailMode() in chrome/test/pyautolib/pyauto.py for
// all possible input values.
// Sample JSON output: {}
void TestingAutomationProvider::SetNTPThumbnailMode(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  std::string section_name;
  bool turn_on;
  if (!args->GetString("section", &section_name) ||
      !args->GetBoolean("turn_on", &turn_on)) {
    reply.SendError("Invalid or missing args");
    return;
  }

  PrefService* prefs = browser->profile()->GetPrefs();
  Section section;
  if (section_name.compare("apps") == 0) {
    section = APPS;
  } else if (section_name.compare("most_visited") == 0) {
    section = THUMB;
  } else if (section_name.compare("recently_closed") == 0) {
    reply.SendError("Thumbnail mode does not apply to the recently closed "
                    "section.");
    return;
  } else {
    reply.SendError(StringPrintf("Unexpected section name: '%s'",
                                 section_name.c_str()));
    return;
  }

  if (turn_on) {
    ShownSectionsHandler::SetShownSection(prefs, section);
  } else {
    int shown_sections = ShownSectionsHandler::GetShownSections(prefs);
    // Change the bit for the relevant section in the bitmask to 0.
    shown_sections &= ~(0xFFFFFFFF & section);
    prefs->SetInteger(prefs::kNTPShownSections, shown_sections);
  }

  reply.SendSuccess(NULL);
}

// Sample JSON input: { "command": "GetNTPMenuMode" }
// For output, refer to GetNTPMenuMode() in
// chrome/test/pyautolib/pyauto.py.
void TestingAutomationProvider::GetNTPMenuMode(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  const int shown_sections = ShownSectionsHandler::GetShownSections(
      browser->profile()->GetPrefs());

  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
  return_value->SetBoolean("apps", shown_sections & MENU_APPS ? true : false);
  return_value->SetBoolean("most_visited",
                           shown_sections & MENU_THUMB ? true : false);
  return_value->SetBoolean("recently_closed",
                           shown_sections & MENU_RECENT ? true : false);

  AutomationJSONReply reply(this, reply_message);
  reply.SendSuccess(return_value.get());
}

// Sample JSON input: { "command": "SetNTPMenuMode", "section": "apps",
//                      "turn_on": false }
// Refer to SetNTPMenuMode() in chrome/test/pyautolib/pyauto.py for all possible
// input values.
// Sample JSON output: {}
void TestingAutomationProvider::SetNTPMenuMode(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  std::string section_name;
  bool turn_on;
  if (!args->GetString("section", &section_name) ||
      !args->GetBoolean("turn_on", &turn_on)) {
    reply.SendError("Invalid or missing args");
    return;
  }

  PrefService* prefs = browser->profile()->GetPrefs();
  Section section;
  if (section_name.compare("apps") == 0) {
    section = MENU_APPS;
  } else if (section_name.compare("most_visited") == 0) {
    section = MENU_THUMB;
  } else if (section_name.compare("recently_closed") == 0) {
    section = MENU_RECENT;
  } else {
    reply.SendError(StringPrintf("Unexpected section name: '%s'",
                                 section_name.c_str()));
    return;
  }

  int shown_sections = ShownSectionsHandler::GetShownSections(prefs);
  if (turn_on) {
    // Change the bit for the relevant section in the bitmask to 1.
    shown_sections |= section;
  } else {
    // Change the bit for the relevant section in the bitmask to 0.
    shown_sections &= ~(0xFFFFFFFF & section);
  }
  prefs->SetInteger(prefs::kNTPShownSections, shown_sections);

  reply.SendSuccess(NULL);
}

// Sample JSON input: { "command": "LaunchApp",
//                      "id": "ahfgeienlihckogmohjhadlkjgocpleb" }
// Sample JSON output: {}
void TestingAutomationProvider::LaunchApp(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  std::string id;
  if (!args->GetString("id", &id)) {
    AutomationJSONReply(this, reply_message).SendError(
        "Must include string id.");
    return;
  }

  ExtensionService* service = browser->profile()->GetExtensionService();
  if (!service) {
    AutomationJSONReply(this, reply_message).SendError(
        "No extensions service.");
    return;
  }

  const Extension* extension = service->GetExtensionById(
      id, false  /* do not include disabled extensions */);
  if (!extension) {
    AutomationJSONReply(this, reply_message).SendError(
        StringPrintf("Extension with ID '%s' doesn't exist or is disabled.",
                     id.c_str()));
    return;
  }

  // Look at preferences to find the right launch container.  If no preference
  // is set, launch as a regular tab.
  extension_misc::LaunchContainer launch_container =
      service->extension_prefs()->GetLaunchContainer(
          extension, ExtensionPrefs::LAUNCH_REGULAR);

  TabContents* old_contents = browser->GetSelectedTabContents();
  if (!old_contents) {
    AutomationJSONReply(this, reply_message).SendError(
        "Cannot identify selected tab contents.");
    return;
  }

  // This observer will delete itself.
  new AppLaunchObserver(&old_contents->controller(), this, reply_message,
                        launch_container);
  Browser::OpenApplication(profile(), extension, launch_container,
                           old_contents);
}

// Sample JSON input: { "command": "SetAppLaunchType",
//                      "id": "ahfgeienlihckogmohjhadlkjgocpleb",
//                      "launch_type": "pinned" }
// Sample JSON output: {}
void TestingAutomationProvider::SetAppLaunchType(
    Browser* browser,
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);

  std::string id;
  if (!args->GetString("id", &id)) {
    reply.SendError("Must include string id.");
    return;
  }

  std::string launch_type_str;
  if (!args->GetString("launch_type", &launch_type_str)) {
    reply.SendError("Must specify app launch type.");
    return;
  }

  ExtensionService* service = browser->profile()->GetExtensionService();
  if (!service) {
    reply.SendError("No extensions service.");
    return;
  }

  const Extension* extension = service->GetExtensionById(
      id, true  /* include disabled extensions */);
  if (!extension) {
    reply.SendError(
        StringPrintf("Extension with ID '%s' doesn't exist.", id.c_str()));
    return;
  }

  ExtensionPrefs::LaunchType launch_type;
  if (launch_type_str == "pinned") {
    launch_type = ExtensionPrefs::LAUNCH_PINNED;
  } else if (launch_type_str == "regular") {
    launch_type = ExtensionPrefs::LAUNCH_REGULAR;
  } else if (launch_type_str == "fullscreen") {
    launch_type = ExtensionPrefs::LAUNCH_FULLSCREEN;
  } else if (launch_type_str == "window") {
    launch_type = ExtensionPrefs::LAUNCH_WINDOW;
  } else {
    reply.SendError(
        StringPrintf("Unexpected launch type '%s'.", launch_type_str.c_str()));
    return;
  }

  service->extension_prefs()->SetLaunchType(extension->id(), launch_type);
  reply.SendSuccess(NULL);
}

void TestingAutomationProvider::WaitForAllTabsToStopLoading(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  // This class will send the message immediately if no tab is loading.
  new AllTabsStoppedLoadingObserver(this, reply_message);
}

void TestingAutomationProvider::GetIndicesFromTab(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  int id_or_handle = 0;
  bool has_id = args->HasKey("tab_id");
  bool has_handle = args->HasKey("tab_handle");
  if (has_id && has_handle) {
    reply.SendError(
        "Both 'tab_id' and 'tab_handle' were specified. Only one is allowed");
    return;
  } else if (!has_id && !has_handle) {
    reply.SendError("Either 'tab_id' or 'tab_handle' must be specified");
    return;
  }
  if (has_id && !args->GetInteger("tab_id", &id_or_handle)) {
    reply.SendError("'tab_id' is invalid");
    return;
  }
  if (has_handle && (!args->GetInteger("tab_handle", &id_or_handle) ||
                     !tab_tracker_->ContainsHandle(id_or_handle))) {
    reply.SendError("'tab_handle' is invalid");
    return;
  }
  int id = id_or_handle;
  if (has_handle)
    id = tab_tracker_->GetResource(id_or_handle)->session_id().id();
  BrowserList::const_iterator iter = BrowserList::begin();
  int browser_index = 0;
  for (; iter != BrowserList::end(); ++iter, ++browser_index) {
    Browser* browser = *iter;
    for (int tab_index = 0; tab_index < browser->tab_count(); ++tab_index) {
      TabContents* tab = browser->GetTabContentsAt(tab_index);
      if (tab->controller().session_id().id() == id) {
        DictionaryValue dict;
        dict.SetInteger("windex", browser_index);
        dict.SetInteger("tab_index", tab_index);
        reply.SendSuccess(&dict);
        return;
      }
    }
  }
  reply.SendError("Could not find tab among current browser windows");
}

void TestingAutomationProvider::NavigateToURL(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  int navigation_count;
  std::string url, error;
  Browser* browser;
  TabContents* tab_contents;
  if (!GetBrowserAndTabFromJSONArgs(args, &browser, &tab_contents, &error)) {
    AutomationJSONReply(this, reply_message).SendError(error);
    return;
  }
  if (!args->GetString("url", &url)) {
    AutomationJSONReply(this, reply_message)
        .SendError("'url' missing or invalid");
    return;
  }
  if (!args->GetInteger("navigation_count", &navigation_count)) {
    AutomationJSONReply(this, reply_message)
        .SendError("'navigation_count' missing or invalid");
    return;
  }
  new NavigationNotificationObserver(
      &tab_contents->controller(), this, reply_message,
      navigation_count, false, true);
  browser->OpenURLFromTab(
      tab_contents, GURL(url), GURL(), CURRENT_TAB, PageTransition::TYPED);
}

void TestingAutomationProvider::ExecuteJavascriptJSON(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  string16 frame_xpath, javascript;
  std::string error;
  TabContents* tab_contents;
  if (!GetTabFromJSONArgs(args, &tab_contents, &error)) {
    AutomationJSONReply(this, reply_message).SendError(error);
    return;
  }
  if (!args->GetString("frame_xpath", &frame_xpath)) {
    AutomationJSONReply(this, reply_message)
        .SendError("'frame_xpath' missing or invalid");
    return;
  }
  if (!args->GetString("javascript", &javascript)) {
    AutomationJSONReply(this, reply_message)
        .SendError("'javascript' missing or invalid");
    return;
  }

  // Set the routing id of this message with the controller.
  // This routing id needs to be remembered for the reverse
  // communication while sending back the response of
  // this javascript execution.
  std::string set_automation_id;
  base::SStringPrintf(&set_automation_id,
                      "window.domAutomationController.setAutomationId(%d);",
                      reply_message->routing_id());

  new DomOperationMessageSender(this, reply_message, true);
  tab_contents->render_view_host()->ExecuteJavascriptInWebFrame(
      frame_xpath, UTF8ToUTF16(set_automation_id));
  tab_contents->render_view_host()->ExecuteJavascriptInWebFrame(
      frame_xpath, javascript);
}

void TestingAutomationProvider::GoForward(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  TabContents* tab_contents;
  std::string error;
  if (!GetTabFromJSONArgs(args, &tab_contents, &error)) {
    AutomationJSONReply(this, reply_message).SendError(error);
    return;
  }
  NavigationController& controller = tab_contents->controller();
  if (!controller.CanGoForward()) {
    DictionaryValue dict;
    dict.SetBoolean("did_go_forward", false);
    AutomationJSONReply(this, reply_message).SendSuccess(&dict);
    return;
  }
  new NavigationNotificationObserver(&controller, this, reply_message,
                                     1, false, true);
  controller.GoForward();
}

void TestingAutomationProvider::GoBack(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  TabContents* tab_contents;
  std::string error;
  if (!GetTabFromJSONArgs(args, &tab_contents, &error)) {
    AutomationJSONReply(this, reply_message).SendError(error);
    return;
  }
  NavigationController& controller = tab_contents->controller();
  if (!controller.CanGoBack()) {
    DictionaryValue dict;
    dict.SetBoolean("did_go_back", false);
    AutomationJSONReply(this, reply_message).SendSuccess(&dict);
    return;
  }
  new NavigationNotificationObserver(&controller, this, reply_message,
                                     1, false, true);
  controller.GoBack();
}

void TestingAutomationProvider::ReloadJSON(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  TabContents* tab_contents;
  std::string error;
  if (!GetTabFromJSONArgs(args, &tab_contents, &error)) {
    AutomationJSONReply(this, reply_message).SendError(error);
    return;
  }
  NavigationController& controller = tab_contents->controller();
  new NavigationNotificationObserver(&controller, this, reply_message,
                                     1, false, true);
  controller.Reload(false);
}

void TestingAutomationProvider::GetTabURLJSON(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  TabContents* tab_contents;
  std::string error;
  if (!GetTabFromJSONArgs(args, &tab_contents, &error)) {
    reply.SendError(error);
    return;
  }
  DictionaryValue dict;
  dict.SetString("url", tab_contents->GetURL().possibly_invalid_spec());
  reply.SendSuccess(&dict);
}

void TestingAutomationProvider::GetTabTitleJSON(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  TabContents* tab_contents;
  std::string error;
  if (!GetTabFromJSONArgs(args, &tab_contents, &error)) {
    reply.SendError(error);
    return;
  }
  DictionaryValue dict;
  dict.SetString("title", tab_contents->GetTitle());
  reply.SendSuccess(&dict);
}

void TestingAutomationProvider::CaptureEntirePageJSON(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  TabContents* tab_contents;
  std::string error;

  if (!GetTabFromJSONArgs(args, &tab_contents, &error)) {
    AutomationJSONReply(this, reply_message).SendError(error);
    return;
  }

  FilePath::StringType path_str;
  if (!args->GetString("path", &path_str)) {
    AutomationJSONReply(this, reply_message)
        .SendError("'path' missing or invalid");
    return;
  }

  RenderViewHost* render_view = tab_contents->render_view_host();
  if (render_view) {
    FilePath path(path_str);
    // This will delete itself when finished.
    PageSnapshotTaker* snapshot_taker = new PageSnapshotTaker(
        this, reply_message, render_view, path);
    snapshot_taker->Start();
  } else {
    AutomationJSONReply(this, reply_message)
        .SendError("Tab has no associated RenderViewHost");
  }
}

void TestingAutomationProvider::GetCookiesJSON(
    DictionaryValue* args, IPC::Message* reply_message) {
  automation_util::GetCookiesJSON(this, args, reply_message);
}

void TestingAutomationProvider::DeleteCookieJSON(
    DictionaryValue* args, IPC::Message* reply_message) {
  automation_util::DeleteCookieJSON(this, args, reply_message);
}

void TestingAutomationProvider::SetCookieJSON(
    DictionaryValue* args, IPC::Message* reply_message) {
  automation_util::SetCookieJSON(this, args, reply_message);
}

void TestingAutomationProvider::GetTabIds(
    DictionaryValue* args, IPC::Message* reply_message) {
  ListValue* id_list = new ListValue();
  BrowserList::const_iterator iter = BrowserList::begin();
  for (; iter != BrowserList::end(); ++iter) {
    Browser* browser = *iter;
    for (int i = 0; i < browser->tab_count(); ++i) {
      int id = browser->GetTabContentsAt(i)->controller().session_id().id();
      id_list->Append(Value::CreateIntegerValue(id));
    }
  }
  DictionaryValue dict;
  dict.Set("ids", id_list);
  AutomationJSONReply(this, reply_message).SendSuccess(&dict);
}

void TestingAutomationProvider::IsTabIdValid(
    DictionaryValue* args, IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  int id;
  if (!args->GetInteger("id", &id)) {
    reply.SendError("'id' missing or invalid");
    return;
  }
  bool is_valid = false;
  BrowserList::const_iterator iter = BrowserList::begin();
  for (; iter != BrowserList::end(); ++iter) {
    Browser* browser = *iter;
    for (int i = 0; i < browser->tab_count(); ++i) {
      TabContents* tab = browser->GetTabContentsAt(i);
      if (tab->controller().session_id().id() == id) {
        is_valid = true;
        break;
      }
    }
  }
  DictionaryValue dict;
  dict.SetBoolean("is_valid", is_valid);
  reply.SendSuccess(&dict);
}

void TestingAutomationProvider::CloseTabJSON(
    DictionaryValue* args, IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  Browser* browser;
  TabContents* tab_contents;
  std::string error;
  if (!GetBrowserAndTabFromJSONArgs(args, &browser, &tab_contents, &error)) {
    reply.SendError(error);
    return;
  }
  browser->CloseTabContents(tab_contents);
  reply.SendSuccess(NULL);
}

void TestingAutomationProvider::ActivateTabJSON(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  AutomationJSONReply reply(this, reply_message);
  Browser* browser;
  TabContents* tab_contents;
  std::string error;
  if (!GetBrowserAndTabFromJSONArgs(args, &browser, &tab_contents, &error)) {
    reply.SendError(error);
    return;
  }
  browser->ActivateTabAt(
      browser->GetIndexOfController(&tab_contents->controller()), true);
  reply.SendSuccess(NULL);
}

// Sample json input: { "command": "UpdateExtensionsNow" }
// Sample json output: {}
void TestingAutomationProvider::UpdateExtensionsNow(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  ExtensionService* service = profile()->GetExtensionService();
  if (!service) {
    AutomationJSONReply(this, reply_message).SendError(
        "No extensions service.");
    return;
  }

  ExtensionUpdater* updater = service->updater();
  if (!updater) {
    AutomationJSONReply(this, reply_message).SendError(
        "No updater for extensions service.");
    return;
  }

  ExtensionProcessManager* manager = profile()->GetExtensionProcessManager();
  if (!manager) {
    AutomationJSONReply(this, reply_message).SendError(
        "No extension process manager.");
    return;
  }

  // Create a new observer that waits until the extensions have been fully
  // updated (we should not send the reply until after all extensions have
  // been updated).  This observer will delete itself.
  new ExtensionsUpdatedObserver(manager, this, reply_message);
  updater->CheckNow();
}

void TestingAutomationProvider::GetChromeDriverAutomationVersion(
    DictionaryValue* args,
    IPC::Message* reply_message) {
  DictionaryValue reply_dict;
  reply_dict.SetInteger("version", automation::kChromeDriverAutomationVersion);
  AutomationJSONReply(this, reply_message).SendSuccess(&reply_dict);
}

void TestingAutomationProvider::WaitForTabCountToBecome(
    int browser_handle,
    int target_tab_count,
    IPC::Message* reply_message) {
  if (!browser_tracker_->ContainsHandle(browser_handle)) {
    AutomationMsg_WaitForTabCountToBecome::WriteReplyParams(reply_message,
                                                            false);
    Send(reply_message);
    return;
  }

  Browser* browser = browser_tracker_->GetResource(browser_handle);

  // The observer will delete itself.
  new TabCountChangeObserver(this, browser, reply_message, target_tab_count);
}

void TestingAutomationProvider::WaitForInfoBarCount(
    int tab_handle,
    size_t target_count,
    IPC::Message* reply_message) {
  if (!tab_tracker_->ContainsHandle(tab_handle)) {
    AutomationMsg_WaitForInfoBarCount::WriteReplyParams(reply_message_, false);
    Send(reply_message_);
    return;
  }

  NavigationController* controller = tab_tracker_->GetResource(tab_handle);
  if (!controller) {
    AutomationMsg_WaitForInfoBarCount::WriteReplyParams(reply_message_, false);
    Send(reply_message_);
    return;
  }

  // The delegate will delete itself.
  new InfoBarCountObserver(this, reply_message, controller->tab_contents(),
                           target_count);
}

// Gets the current used encoding name of the page in the specified tab.
void TestingAutomationProvider::GetPageCurrentEncoding(
    int tab_handle, std::string* current_encoding) {
  if (tab_tracker_->ContainsHandle(tab_handle)) {
    NavigationController* nav = tab_tracker_->GetResource(tab_handle);
    Browser* browser = FindAndActivateTab(nav);
    DCHECK(browser);

    if (browser->command_updater()->IsCommandEnabled(IDC_ENCODING_MENU))
      *current_encoding = nav->tab_contents()->encoding();
  }
}

void TestingAutomationProvider::ShutdownSessionService(int handle,
                                                       bool* result) {
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    browser->profile()->ShutdownSessionService();
    *result = true;
  } else {
    *result = false;
  }
}

void TestingAutomationProvider::SetContentSetting(
    int handle,
    const std::string& host,
    ContentSettingsType content_type,
    ContentSetting setting,
    bool* success) {
  *success = false;
  if (browser_tracker_->ContainsHandle(handle)) {
    Browser* browser = browser_tracker_->GetResource(handle);
    HostContentSettingsMap* map =
        browser->profile()->GetHostContentSettingsMap();
    if (host.empty()) {
      map->SetDefaultContentSetting(content_type, setting);
    } else {
      map->SetContentSetting(ContentSettingsPattern(host),
                             content_type, "", setting);
    }
    *success = true;
  }
}

void TestingAutomationProvider::LoadBlockedPlugins(int tab_handle,
                                                   bool* success) {
  *success = false;
  if (tab_tracker_->ContainsHandle(tab_handle)) {
    NavigationController* nav = tab_tracker_->GetResource(tab_handle);
    if (!nav)
      return;
    TabContents* contents = nav->tab_contents();
    if (!contents)
      return;
    contents->render_view_host()->LoadBlockedPlugins();
    *success = true;
  }
}

void TestingAutomationProvider::ResetToDefaultTheme() {
  ThemeServiceFactory::GetForProfile(profile_)->UseDefaultTheme();
}

void TestingAutomationProvider::WaitForProcessLauncherThreadToGoIdle(
    IPC::Message* reply_message) {
  new WaitForProcessLauncherThreadToGoIdleObserver(this, reply_message);
}

void TestingAutomationProvider::GetParentBrowserOfTab(int tab_handle,
                                                      int* browser_handle,
                                                      bool* success) {
  *success = false;
  if (tab_tracker_->ContainsHandle(tab_handle)) {
    NavigationController* controller = tab_tracker_->GetResource(tab_handle);
    int index;
    Browser* browser = Browser::GetBrowserForController(controller, &index);
    if (browser) {
      *browser_handle = browser_tracker_->Add(browser);
      *success = true;
    }
  }
}

// TODO(brettw) change this to accept GURLs when history supports it
void TestingAutomationProvider::OnRedirectQueryComplete(
    HistoryService::Handle request_handle,
    GURL from_url,
    bool success,
    history::RedirectList* redirects) {
  DCHECK_EQ(redirect_query_, request_handle);
  DCHECK(reply_message_ != NULL);

  std::vector<GURL> redirects_gurl;
  reply_message_->WriteBool(success);
  if (success) {
    for (size_t i = 0; i < redirects->size(); i++)
      redirects_gurl.push_back(redirects->at(i));
  }

  IPC::ParamTraits<std::vector<GURL> >::Write(reply_message_, redirects_gurl);

  Send(reply_message_);
  redirect_query_ = 0;
  reply_message_ = NULL;
}

void TestingAutomationProvider::OnRemoveProvider() {
  AutomationProviderList::GetInstance()->RemoveProvider(this);
}