diff options
Diffstat (limited to 'chrome/browser/automation/testing_automation_provider.cc')
-rw-r--r-- | chrome/browser/automation/testing_automation_provider.cc | 1770 |
1 files changed, 1770 insertions, 0 deletions
diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc index b5cb004..89c69f0 100644 --- a/chrome/browser/automation/testing_automation_provider.cc +++ b/chrome/browser/automation/testing_automation_provider.cc @@ -6,14 +6,20 @@ #include "app/message_box_flags.h" #include "base/command_line.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/time.h" #include "base/utf_string_conversions.h" #include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/app_modal_dialog.h" #include "chrome/browser/app_modal_dialog_queue.h" #include "chrome/browser/autocomplete/autocomplete_edit.h" +#include "chrome/browser/autofill/autofill_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" @@ -27,16 +33,22 @@ #include "chrome/browser/debugger/devtools_manager.h" #include "chrome/browser/download/download_prefs.h" #include "chrome/browser/download/download_shelf.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/find_bar.h" #include "chrome/browser/location_bar.h" #include "chrome/browser/login_prompt.h" #include "chrome/browser/profile.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profile_manager.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/tab_contents/infobar_delegate.h" #include "chrome/browser/tab_contents/interstitial_page.h" +#include "chrome/browser/translate/translate_infobar_delegate.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/net/url_request_context_getter.h" #include "chrome/common/notification_service.h" @@ -45,6 +57,7 @@ #include "net/base/cookie_store.h" #include "net/url_request/url_request_context.h" #include "views/event.h" +#include "webkit/glue/plugins/plugin_list.h" namespace { @@ -388,6 +401,8 @@ void TestingAutomationProvider::OnMessageReceived( 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_UNHANDLED(AutomationProvider::OnMessageReceived(message)); IPC_END_MESSAGE_MAP() @@ -1919,6 +1934,1761 @@ void TestingAutomationProvider::GetBlockedPopupCount(int handle, int* count) { } } +void TestingAutomationProvider::SendJSONRequest(int handle, + std::string json_request, + IPC::Message* reply_message) { + Browser* browser = NULL; + scoped_ptr<Value> values; + + // Basic error checking. + if (browser_tracker_->ContainsHandle(handle)) { + browser = browser_tracker_->GetResource(handle); + } + if (!browser) { + AutomationJSONReply(this, reply_message).SendError("no browser object"); + return; + } + 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["DisablePlugin"] = &TestingAutomationProvider::DisablePlugin; + handler_map["EnablePlugin"] = &TestingAutomationProvider::EnablePlugin; + handler_map["GetPluginsInfo"] = &TestingAutomationProvider::GetPluginsInfo; + + handler_map["GetBrowserInfo"] = &TestingAutomationProvider::GetBrowserInfo; + + handler_map["GetNavigationInfo"] = + &TestingAutomationProvider::GetNavigationInfo; + + handler_map["PerformActionOnInfobar"] = + &TestingAutomationProvider::PerformActionOnInfobar; + + handler_map["GetHistoryInfo"] = &TestingAutomationProvider::GetHistoryInfo; + handler_map["AddHistoryItem"] = &TestingAutomationProvider::AddHistoryItem; + + handler_map["GetOmniboxInfo"] = &TestingAutomationProvider::GetOmniboxInfo; + handler_map["SetOmniboxText"] = &TestingAutomationProvider::SetOmniboxText; + handler_map["OmniboxAcceptInput"] = + &TestingAutomationProvider::OmniboxAcceptInput; + handler_map["OmniboxMovePopupSelection"] = + &TestingAutomationProvider::OmniboxMovePopupSelection; + + handler_map["GetPrefsInfo"] = &TestingAutomationProvider::GetPrefsInfo; + handler_map["SetPrefs"] = &TestingAutomationProvider::SetPrefs; + + handler_map["SetWindowDimensions"] = + &TestingAutomationProvider::SetWindowDimensions; + + handler_map["GetDownloadsInfo"] = + &TestingAutomationProvider::GetDownloadsInfo; + handler_map["WaitForAllDownloadsToComplete"] = + &TestingAutomationProvider::WaitForDownloadsToComplete; + handler_map["PerformActionOnDownload"] = + &TestingAutomationProvider::PerformActionOnDownload; + + handler_map["GetInitialLoadTimes"] = + &TestingAutomationProvider::GetInitialLoadTimes; + + handler_map["SaveTabContents"] = &TestingAutomationProvider::SaveTabContents; + + handler_map["ImportSettings"] = &TestingAutomationProvider::ImportSettings; + + handler_map["AddSavedPassword"] = + &TestingAutomationProvider::AddSavedPassword; + handler_map["RemoveSavedPassword"] = + &TestingAutomationProvider::RemoveSavedPassword; + handler_map["GetSavedPasswords"] = + &TestingAutomationProvider::GetSavedPasswords; + + handler_map["ClearBrowsingData"] = + &TestingAutomationProvider::ClearBrowsingData; + + // SetTheme() implemented using InstallExtension(). + handler_map["GetThemeInfo"] = &TestingAutomationProvider::GetThemeInfo; + + // InstallExtension() present in pyauto.py. + handler_map["GetExtensionsInfo"] = + &TestingAutomationProvider::GetExtensionsInfo; + handler_map["UninstallExtensionById"] = + &TestingAutomationProvider::UninstallExtensionById; + + handler_map["SelectTranslateOption"] = + &TestingAutomationProvider::SelectTranslateOption; + handler_map["GetTranslateInfo"] = + &TestingAutomationProvider::GetTranslateInfo; + + handler_map["GetAutoFillProfile"] = + &TestingAutomationProvider::GetAutoFillProfile; + handler_map["FillAutoFillProfile"] = + &TestingAutomationProvider::FillAutoFillProfile; + + if (handler_map.find(std::string(command)) != handler_map.end()) { + (this->*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 + ", "; + } + 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 (int infobar_index = 0; + infobar_index < tc->infobar_delegate_count(); + ++infobar_index) { + DictionaryValue* infobar_item = new DictionaryValue; + InfoBarDelegate* infobar = tc->GetInfoBarDelegateAt(infobar_index); + if (infobar->AsConfirmInfoBarDelegate()) { + // Also covers ThemeInstalledInfoBarDelegate and + // CrashedExtensionInfoBarDelegate. + 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 (ConfirmInfoBarDelegate::BUTTON_OK & buttons) { + StringValue* button_label = new StringValue( + confirm_infobar->GetButtonLabel( + ConfirmInfoBarDelegate::BUTTON_OK)); + buttons_list->Append(button_label); + } + if (ConfirmInfoBarDelegate::BUTTON_CANCEL & buttons) { + 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->AsAlertInfoBarDelegate()) { + infobar_item->SetString("type", "alert_infobar"); + AlertInfoBarDelegate* alert_infobar = + infobar->AsAlertInfoBarDelegate(); + infobar_item->SetString("text", alert_infobar->GetMessageText()); + } 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; + std::string action; + if (!args->GetInteger("tab_index", &tab_index) || + !args->GetInteger("infobar_index", &infobar_index) || + !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; + if (infobar_index < 0 || + infobar_index >= tab_contents->infobar_delegate_count() || + !(infobar = tab_contents->GetInfoBarDelegateAt(infobar_index))) { + reply.SendError(StringPrintf("No such infobar at index %d", 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(ChromeThread::CurrentlyOn(ChromeThread::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) { + DictionaryValue* properties = new DictionaryValue; + properties->SetString("ChromeVersion", chrome::kChromeVersion); + properties->SetString("BrowserProcessExecutableName", + WideToUTF16Hack(chrome::kBrowserProcessExecutableName)); + properties->SetString("HelperProcessExecutableName", + WideToUTF16Hack(chrome::kHelperProcessExecutableName)); + properties->SetString("BrowserProcessExecutablePath", + WideToUTF16Hack(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->selected_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)); + 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(ChromeThread::PostTask( + ChromeThread::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(); + for (ProfileManager::const_iterator it = profile_manager->begin(); + it != profile_manager->end(); ++it) { + ExtensionProcessManager* process_manager = + (*it)->GetExtensionProcessManager(); + 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<NavigationEntry::PageType, std::string> pagetype_to_string; + pagetype_to_string[NavigationEntry::NORMAL_PAGE] = "NORMAL_PAGE"; + pagetype_to_string[NavigationEntry::ERROR_PAGE] = "ERROR_PAGE"; + pagetype_to_string[NavigationEntry::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->GetReal("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); + + if (!profile_->HasCreatedDownloadManager()) { + reply.SendError("no download manager"); + return; + } + + scoped_ptr<DictionaryValue> return_value(new DictionaryValue); + std::vector<DownloadItem*> downloads; + profile_->GetDownloadManager()->GetAllDownloads(FilePath(), &downloads); + + ListValue* list_of_downloads = new ListValue; + 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()); + // All value objects allocated above are owned by |return_value| + // and get freed by it. +} + +void TestingAutomationProvider::WaitForDownloadsToComplete( + Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + AutomationJSONReply reply(this, reply_message); + + // Look for a quick return. + if (!profile_->HasCreatedDownloadManager()) { + reply.SendSuccess(NULL); // No download manager. + return; + } + std::vector<DownloadItem*> downloads; + profile_->GetDownloadManager()->GetCurrentDownloads(FilePath(), &downloads); + if (downloads.empty()) { + reply.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 (!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 = 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(false); + } else if (action == "decline_dangerous_download") { + // This is the same as removing the file with delete_file=true. + download_manager->AddObserver( + new AutomationProviderDownloadModelChangedObserver( + this, reply_message, download_manager)); + selected_item->Remove(true); + } 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": "GetPrefsInfo" } +// Refer chrome/test/pyautolib/prefs_info.py for sample json output. +void TestingAutomationProvider::GetPrefsInfo(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + const PrefService::PreferenceSet& prefs = + profile_->GetPrefs()->preference_set(); + DictionaryValue* items = new DictionaryValue; + for (PrefService::PreferenceSet::const_iterator it = prefs.begin(); + it != prefs.end(); ++it) { + items->Set((*it)->name(), (*it)->GetValue()->DeepCopy()); + } + 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", WideToUTF16Hack(match.contents)); + item->SetString("description", WideToUTF16Hack(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->query_in_progress()); + properties->SetString("keyword", WideToUTF16Hack(model->keyword())); + properties->SetString("text", WideToUTF16Hack(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(UTF16ToWideHack(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(); + // Setup observer to wait until the selected item loads. + NotificationObserver* observer = + new OmniboxAcceptNotificationObserver(&controller, this, reply_message); + notification_observer_list_.AddObserver(observer); + + browser->window()->GetLocationBar()->AcceptInput(); +} + +// 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<WebPluginInfo> plugins; + NPAPI::PluginList::Singleton()->GetPlugins(false, &plugins); + ListValue* items = new ListValue; + for (std::vector<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", it->enabled); + // Add info about mime types. + ListValue* mime_types = new ListValue(); + for (std::vector<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 (!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 (!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; + TabContents* 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->GetTabContentsAt(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->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->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, 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; + + string16 browser_name; + int import_items = 0; + ListValue* import_items_list = NULL; + bool first_run; + + if (!args->GetString("import_from", &browser_name) || + !args->GetBoolean("first_run", &first_run) || + !args->GetList("import_items", &import_items_list)) { + AutomationJSONReply(this, reply_message).SendError( + "Incorrect type for one or more of the arguments."); + return; + } + + 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_items |= string_to_import_item[item]; + } + + ImporterHost* importer_host = new ImporterHost(); + // Get the correct ProfileInfo based on the browser they user provided. + importer::ProfileInfo profile_info; + int num_browsers = importer_host->GetAvailableProfileCount(); + int i = 0; + for ( ; i < num_browsers; i++) { + string16 name = WideToUTF16Hack(importer_host->GetSourceProfileNameAt(i)); + if (name == browser_name) { + profile_info = importer_host->GetSourceProfileInfoAt(i); + break; + } + } + // If we made it to the end of the loop, then the input was bad. + if (i == num_browsers) { + AutomationJSONReply(this, reply_message).SendError( + "Invalid browser name string found."); + return; + } + + Profile* profile = browser->profile(); + + importer_host->SetObserver( + new AutomationProviderImportSettingsObserver(this, reply_message)); + importer_host->StartImportSettings(profile_info, profile, import_items, + new ProfileWriter(profile), first_run); +} + +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.GetReal("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 off the record. + 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 off the record. + 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 retreived off the record. + 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 (int i = 0; i < tab_contents->infobar_delegate_count(); i++) { + InfoBarDelegate* infobar = tab_contents->GetInfoBarDelegateAt(i); + if (infobar->AsTranslateInfoBarDelegate()) + return infobar->AsTranslateInfoBarDelegate(); + } + // No translate infobar. + return NULL; + } + +} // namespace + +// 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. + std::string language = tab_contents->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. + int target_language_index = -1; + for (int i = 0; i < translate_bar->GetLanguageCount(); i++) { + if (translate_bar->GetLanguageDisplayableNameAt(i) == target_language) { + target_language_index = i; + break; + } + } + if (target_language_index == -1) { + 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": "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); + Extension* theme = browser->profile()->GetTheme(); + 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()); +} + +// 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); + ExtensionsService* service = profile()->GetExtensionsService(); + 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()); + 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) { + AutomationJSONReply reply(this, reply_message); + std::string id; + if (!args->GetString("id", &id)) { + reply.SendError("Must include string id."); + return; + } + ExtensionsService* service = profile()->GetExtensionsService(); + if (!service) { + reply.SendError("No extensions service."); + return; + } + ExtensionUnloadNotificationObserver observer; + service->UninstallExtension(id, false); + reply.SendSuccess(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) { + // Get the AutoFillProfiles currently in the database. + int tab_index = 0; + args->GetInteger("tab_index", &tab_index); + TabContents* tab_contents = browser->GetTabContentsAt(tab_index); + AutomationJSONReply reply(this, reply_message); + + 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; + 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; + args->GetInteger("tab_index", &tab_index); + TabContents* tab_contents = browser->GetTabContentsAt(tab_index); + + if (tab_contents) { + PersonalDataManager* pdm = tab_contents->profile() + ->GetPersonalDataManager(); + if (pdm) { + pdm->OnAutoFillDialogApply(profiles? &autofill_profiles : NULL, + cards? &credit_cards : NULL); + } else { + reply.SendError("No PersonalDataManager."); + return; + } + } else { + reply.SendError("No tab at that index."); + return; + } + reply.SendSuccess(NULL); +} + +/* static */ +ListValue* TestingAutomationProvider::GetListFromAutoFillProfiles( + std::vector<AutoFillProfile*> autofill_profiles) { + ListValue* profiles = new ListValue; + + std::map<AutoFillFieldType, std::wstring> autofill_type_to_string + = GetAutoFillFieldToStringMap(); + + // For each AutoFillProfile, transform it to a dictionary object to return. + for (std::vector<AutoFillProfile*>::iterator it = autofill_profiles.begin(); + it != autofill_profiles.end(); ++it) { + AutoFillProfile* profile = *it; + DictionaryValue* profile_info = new DictionaryValue; + profile_info->SetString("label", profile->Label()); + // For each of the types, if it has a value, add it to the dictionary. + for (std::map<AutoFillFieldType, std::wstring>::iterator + type_it = autofill_type_to_string.begin(); + type_it != autofill_type_to_string.end(); ++type_it) { + string16 value = profile->GetFieldText(AutoFillType(type_it->first)); + if (value.length()) { // If there was something stored for that value. + profile_info->SetString(WideToUTF8(type_it->second), value); + } + } + profiles->Append(profile_info); + } + return profiles; +} + +/* static */ +ListValue* TestingAutomationProvider::GetListFromCreditCards( + std::vector<CreditCard*> credit_cards) { + ListValue* cards = new ListValue; + + std::map<AutoFillFieldType, std::wstring> credit_card_type_to_string = + GetCreditCardFieldToStringMap(); + + // For each AutoFillProfile, transform it to a dictionary object to return. + for (std::vector<CreditCard*>::iterator it = credit_cards.begin(); + it != credit_cards.end(); ++it) { + CreditCard* card = *it; + DictionaryValue* card_info = new DictionaryValue; + card_info->SetString("label", card->Label()); + // For each of the types, if it has a value, add it to the dictionary. + for (std::map<AutoFillFieldType, std::wstring>::iterator type_it = + credit_card_type_to_string.begin(); + type_it != credit_card_type_to_string.end(); ++type_it) { + string16 value = card->GetFieldText(AutoFillType(type_it->first)); + // If there was something stored for that value. + if (value.length()) { + card_info->SetString(WideToUTF8(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 profile_label; + string16 current_value; + + std::map<AutoFillFieldType, std::wstring> autofill_type_to_string = + GetAutoFillFieldToStringMap(); + + int num_profiles = profiles.GetSize(); + for (int i = 0; i < num_profiles; i++) { + profiles.GetDictionary(i, &profile_info); + profile_info->GetString("label", &profile_label); + // Choose an id of 0 so that a unique id will be created. + AutoFillProfile profile(profile_label, 0); + // Loop through the possible profile types and add those provided. + for (std::map<AutoFillFieldType, std::wstring>::iterator type_it = + autofill_type_to_string.begin(); + type_it != autofill_type_to_string.end(); ++type_it) { + if (profile_info->HasKey(WideToUTF8(type_it->second))) { + if (profile_info->GetString(WideToUTF8(type_it->second), + ¤t_value)) { + profile.SetInfo(AutoFillType(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 card_label; + string16 current_value; + + std::map<AutoFillFieldType, std::wstring> 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); + card_info->GetString("label", &card_label); + CreditCard card(card_label, 0); + // Loop through the possible credit card fields and add those provided. + for (std::map<AutoFillFieldType, std::wstring>::iterator type_it = + credit_card_type_to_string.begin(); + type_it != credit_card_type_to_string.end(); ++type_it) { + if (card_info->HasKey(WideToUTF8(type_it->second))) { + if (card_info->GetString(WideToUTF8(type_it->second), ¤t_value)) { + card.SetInfo(AutoFillType(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::wstring> + TestingAutomationProvider::GetAutoFillFieldToStringMap() { + std::map<AutoFillFieldType, std::wstring> autofill_type_to_string; + autofill_type_to_string[NAME_FIRST] = L"NAME_FIRST"; + autofill_type_to_string[NAME_MIDDLE] = L"NAME_MIDDLE"; + autofill_type_to_string[NAME_LAST] = L"NAME_LAST"; + autofill_type_to_string[COMPANY_NAME] = L"COMPANY_NAME"; + autofill_type_to_string[EMAIL_ADDRESS] = L"EMAIL_ADDRESS"; + autofill_type_to_string[ADDRESS_HOME_LINE1] = L"ADDRESS_HOME_LINE1"; + autofill_type_to_string[ADDRESS_HOME_LINE2] = L"ADDRESS_HOME_LINE2"; + autofill_type_to_string[ADDRESS_HOME_CITY] = L"ADDRESS_HOME_CITY"; + autofill_type_to_string[ADDRESS_HOME_STATE] = L"ADDRESS_HOME_STATE"; + autofill_type_to_string[ADDRESS_HOME_ZIP] = L"ADDRESS_HOME_ZIP"; + autofill_type_to_string[ADDRESS_HOME_COUNTRY] = L"ADDRESS_HOME_COUNTRY"; + autofill_type_to_string[PHONE_HOME_NUMBER] = L"PHONE_HOME_NUMBER"; + autofill_type_to_string[PHONE_FAX_NUMBER] = L"PHONE_FAX_NUMBER"; + autofill_type_to_string[NAME_FIRST] = L"NAME_FIRST"; + return autofill_type_to_string; +} + +/* static */ +std::map<AutoFillFieldType, std::wstring> + TestingAutomationProvider::GetCreditCardFieldToStringMap() { + std::map<AutoFillFieldType, std::wstring> credit_card_type_to_string; + credit_card_type_to_string[CREDIT_CARD_NAME] = L"CREDIT_CARD_NAME"; + credit_card_type_to_string[CREDIT_CARD_NUMBER] = L"CREDIT_CARD_NUMBER"; + credit_card_type_to_string[CREDIT_CARD_EXP_MONTH] = L"CREDIT_CARD_EXP_MONTH"; + credit_card_type_to_string[CREDIT_CARD_EXP_4_DIGIT_YEAR] = + L"CREDIT_CARD_EXP_4_DIGIT_YEAR"; + return credit_card_type_to_string; +} + // TODO(brettw) change this to accept GURLs when history supports it void TestingAutomationProvider::OnRedirectQueryComplete( HistoryService::Handle request_handle, |