// Copyright (c) 2012 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/test/base/ui_test_utils.h" #if defined(OS_WIN) #include <windows.h> #endif #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" #include "base/command_line.h" #include "base/file_util.h" #include "base/files/file_path.h" #include "base/json/json_reader.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/path_service.h" #include "base/prefs/pref_service.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/test_timeouts.h" #include "base/time/time.h" #include "base/values.h" #include "chrome/browser/autocomplete/autocomplete_controller.h" #include "chrome/browser/bookmarks/bookmark_model_factory.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/devtools/devtools_window.h" #include "chrome/browser/extensions/extension_action.h" #include "chrome/browser/history/history_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url_service.h" #include "chrome/browser/search_engines/template_url_service_test_util.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/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_iterator.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/find_bar/find_notification_details.h" #include "chrome/browser/ui/find_bar/find_tab_helper.h" #include "chrome/browser/ui/host_desktop.h" #include "chrome/browser/ui/omnibox/location_bar.h" #include "chrome/browser/ui/omnibox/omnibox_view.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/find_in_page_observer.h" #include "components/bookmarks/browser/bookmark_model.h" #include "content/public/browser/dom_operation_notification_details.h" #include "content/public/browser/download_item.h" #include "content/public/browser/download_manager.h" #include "content/public/browser/geolocation_provider.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/common/geoposition.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/download_test_observer.h" #include "content/public/test/test_navigation_observer.h" #include "content/public/test/test_utils.h" #include "net/base/filename_util.h" #include "net/cookies/cookie_constants.h" #include "net/cookies/cookie_monster.h" #include "net/cookies/cookie_store.h" #include "net/test/python_utils.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/size.h" #include "ui/snapshot/test/snapshot_desktop.h" #if defined(USE_AURA) #include "ash/shell.h" #include "ui/aura/window_event_dispatcher.h" #endif using content::DomOperationNotificationDetails; using content::NativeWebKeyboardEvent; using content::NavigationController; using content::NavigationEntry; using content::OpenURLParams; using content::RenderViewHost; using content::RenderWidgetHost; using content::Referrer; using content::WebContents; namespace ui_test_utils { namespace { #if defined(OS_WIN) const char kSnapshotBaseName[] = "ChromiumSnapshot"; const char kSnapshotExtension[] = ".png"; base::FilePath GetSnapshotFileName(const base::FilePath& snapshot_directory) { base::Time::Exploded the_time; base::Time::Now().LocalExplode(&the_time); std::string filename(base::StringPrintf("%s%04d%02d%02d%02d%02d%02d%s", kSnapshotBaseName, the_time.year, the_time.month, the_time.day_of_month, the_time.hour, the_time.minute, the_time.second, kSnapshotExtension)); base::FilePath snapshot_file = snapshot_directory.AppendASCII(filename); if (base::PathExists(snapshot_file)) { int index = 0; std::string suffix; base::FilePath trial_file; do { suffix = base::StringPrintf(" (%d)", ++index); trial_file = snapshot_file.InsertBeforeExtensionASCII(suffix); } while (base::PathExists(trial_file)); snapshot_file = trial_file; } return snapshot_file; } #endif // defined(OS_WIN) Browser* WaitForBrowserNotInSet(std::set<Browser*> excluded_browsers) { Browser* new_browser = GetBrowserNotInSet(excluded_browsers); if (new_browser == NULL) { BrowserAddedObserver observer; new_browser = observer.WaitForSingleNewBrowser(); // The new browser should never be in |excluded_browsers|. DCHECK(!ContainsKey(excluded_browsers, new_browser)); } return new_browser; } } // namespace bool GetCurrentTabTitle(const Browser* browser, base::string16* title) { WebContents* web_contents = browser->tab_strip_model()->GetActiveWebContents(); if (!web_contents) return false; NavigationEntry* last_entry = web_contents->GetController().GetActiveEntry(); if (!last_entry) return false; title->assign(last_entry->GetTitleForDisplay(std::string())); return true; } Browser* OpenURLOffTheRecord(Profile* profile, const GURL& url) { chrome::HostDesktopType active_desktop = chrome::GetActiveDesktop(); chrome::OpenURLOffTheRecord(profile, url, active_desktop); Browser* browser = chrome::FindTabbedBrowser( profile->GetOffTheRecordProfile(), false, active_desktop); content::TestNavigationObserver observer( browser->tab_strip_model()->GetActiveWebContents()); observer.Wait(); return browser; } void NavigateToURL(chrome::NavigateParams* params) { chrome::Navigate(params); content::WaitForLoadStop(params->target_contents); } void NavigateToURLWithPost(Browser* browser, const GURL& url) { chrome::NavigateParams params(browser, url, content::PAGE_TRANSITION_FORM_SUBMIT); params.uses_post = true; NavigateToURL(¶ms); } void NavigateToURL(Browser* browser, const GURL& url) { NavigateToURLWithDisposition(browser, url, CURRENT_TAB, BROWSER_TEST_WAIT_FOR_NAVIGATION); } // Navigates the specified tab (via |disposition|) of |browser| to |url|, // blocking until the |number_of_navigations| specified complete. // |disposition| indicates what tab the download occurs in, and // |browser_test_flags| controls what to wait for before continuing. static void NavigateToURLWithDispositionBlockUntilNavigationsComplete( Browser* browser, const GURL& url, int number_of_navigations, WindowOpenDisposition disposition, int browser_test_flags) { TabStripModel* tab_strip = browser->tab_strip_model(); if (disposition == CURRENT_TAB && tab_strip->GetActiveWebContents()) content::WaitForLoadStop(tab_strip->GetActiveWebContents()); content::TestNavigationObserver same_tab_observer( tab_strip->GetActiveWebContents(), number_of_navigations); std::set<Browser*> initial_browsers; for (chrome::BrowserIterator it; !it.done(); it.Next()) initial_browsers.insert(*it); content::WindowedNotificationObserver tab_added_observer( chrome::NOTIFICATION_TAB_ADDED, content::NotificationService::AllSources()); browser->OpenURL(OpenURLParams( url, Referrer(), disposition, content::PAGE_TRANSITION_TYPED, false)); if (browser_test_flags & BROWSER_TEST_WAIT_FOR_BROWSER) browser = WaitForBrowserNotInSet(initial_browsers); if (browser_test_flags & BROWSER_TEST_WAIT_FOR_TAB) tab_added_observer.Wait(); if (!(browser_test_flags & BROWSER_TEST_WAIT_FOR_NAVIGATION)) { // Some other flag caused the wait prior to this. return; } WebContents* web_contents = NULL; if (disposition == NEW_BACKGROUND_TAB) { // We've opened up a new tab, but not selected it. TabStripModel* tab_strip = browser->tab_strip_model(); web_contents = tab_strip->GetWebContentsAt(tab_strip->active_index() + 1); EXPECT_TRUE(web_contents != NULL) << " Unable to wait for navigation to \"" << url.spec() << "\" because the new tab is not available yet"; if (!web_contents) return; } else if ((disposition == CURRENT_TAB) || (disposition == NEW_FOREGROUND_TAB) || (disposition == SINGLETON_TAB)) { // The currently selected tab is the right one. web_contents = browser->tab_strip_model()->GetActiveWebContents(); } if (disposition == CURRENT_TAB) { same_tab_observer.Wait(); return; } else if (web_contents) { content::TestNavigationObserver observer(web_contents, number_of_navigations); observer.Wait(); return; } EXPECT_TRUE(NULL != web_contents) << " Unable to wait for navigation to \"" << url.spec() << "\"" << " because we can't get the tab contents"; } void NavigateToURLWithDisposition(Browser* browser, const GURL& url, WindowOpenDisposition disposition, int browser_test_flags) { NavigateToURLWithDispositionBlockUntilNavigationsComplete( browser, url, 1, disposition, browser_test_flags); } void NavigateToURLBlockUntilNavigationsComplete(Browser* browser, const GURL& url, int number_of_navigations) { NavigateToURLWithDispositionBlockUntilNavigationsComplete( browser, url, number_of_navigations, CURRENT_TAB, BROWSER_TEST_WAIT_FOR_NAVIGATION); } void WaitUntilDevToolsWindowLoaded(DevToolsWindow* window) { scoped_refptr<content::MessageLoopRunner> runner = new content::MessageLoopRunner; window->SetLoadCompletedCallback(runner->QuitClosure()); runner->Run(); } base::FilePath GetTestFilePath(const base::FilePath& dir, const base::FilePath& file) { base::FilePath path; PathService::Get(chrome::DIR_TEST_DATA, &path); return path.Append(dir).Append(file); } GURL GetTestUrl(const base::FilePath& dir, const base::FilePath& file) { return net::FilePathToFileURL(GetTestFilePath(dir, file)); } bool GetRelativeBuildDirectory(base::FilePath* build_dir) { // This function is used to find the build directory so TestServer can serve // built files (nexes, etc). TestServer expects a path relative to the source // root. base::FilePath exe_dir = CommandLine::ForCurrentProcess()->GetProgram().DirName(); base::FilePath src_dir; if (!PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)) return false; // We must first generate absolute paths to SRC and EXE and from there // generate a relative path. if (!exe_dir.IsAbsolute()) exe_dir = base::MakeAbsoluteFilePath(exe_dir); if (!src_dir.IsAbsolute()) src_dir = base::MakeAbsoluteFilePath(src_dir); if (!exe_dir.IsAbsolute()) return false; if (!src_dir.IsAbsolute()) return false; size_t match, exe_size, src_size; std::vector<base::FilePath::StringType> src_parts, exe_parts; // Determine point at which src and exe diverge. exe_dir.GetComponents(&exe_parts); src_dir.GetComponents(&src_parts); exe_size = exe_parts.size(); src_size = src_parts.size(); for (match = 0; match < exe_size && match < src_size; ++match) { if (exe_parts[match] != src_parts[match]) break; } // Create a relative path. *build_dir = base::FilePath(); for (size_t tmp_itr = match; tmp_itr < src_size; ++tmp_itr) *build_dir = build_dir->Append(FILE_PATH_LITERAL("..")); for (; match < exe_size; ++match) *build_dir = build_dir->Append(exe_parts[match]); return true; } AppModalDialog* WaitForAppModalDialog() { AppModalDialogQueue* dialog_queue = AppModalDialogQueue::GetInstance(); if (dialog_queue->HasActiveDialog()) return dialog_queue->active_dialog(); content::WindowedNotificationObserver observer( chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN, content::NotificationService::AllSources()); observer.Wait(); return content::Source<AppModalDialog>(observer.source()).ptr(); } int FindInPage(WebContents* tab, const base::string16& search_string, bool forward, bool match_case, int* ordinal, gfx::Rect* selection_rect) { FindTabHelper* find_tab_helper = FindTabHelper::FromWebContents(tab); find_tab_helper->StartFinding(search_string, forward, match_case); FindInPageNotificationObserver observer(tab); observer.Wait(); if (ordinal) *ordinal = observer.active_match_ordinal(); if (selection_rect) *selection_rect = observer.selection_rect(); return observer.number_of_matches(); } void WaitForTemplateURLServiceToLoad(TemplateURLService* service) { if (service->loaded()) return; scoped_refptr<content::MessageLoopRunner> message_loop_runner = new content::MessageLoopRunner; scoped_ptr<TemplateURLService::Subscription> subscription = service->RegisterOnLoadedCallback( message_loop_runner->QuitClosure()); service->Load(); message_loop_runner->Run(); ASSERT_TRUE(service->loaded()); } void WaitForHistoryToLoad(HistoryService* history_service) { content::WindowedNotificationObserver history_loaded_observer( chrome::NOTIFICATION_HISTORY_LOADED, content::NotificationService::AllSources()); if (!history_service->BackendLoaded()) history_loaded_observer.Wait(); } void DownloadURL(Browser* browser, const GURL& download_url) { base::ScopedTempDir downloads_directory; ASSERT_TRUE(downloads_directory.CreateUniqueTempDir()); browser->profile()->GetPrefs()->SetFilePath( prefs::kDownloadDefaultDirectory, downloads_directory.path()); content::DownloadManager* download_manager = content::BrowserContext::GetDownloadManager(browser->profile()); scoped_ptr<content::DownloadTestObserver> observer( new content::DownloadTestObserverTerminal( download_manager, 1, content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT)); ui_test_utils::NavigateToURL(browser, download_url); observer->WaitForFinished(); } void SendToOmniboxAndSubmit(LocationBar* location_bar, const std::string& input) { OmniboxView* omnibox = location_bar->GetOmniboxView(); omnibox->model()->OnSetFocus(false); omnibox->SetUserText(base::ASCIIToUTF16(input)); location_bar->AcceptInput(); while (!omnibox->model()->autocomplete_controller()->done()) { content::WindowedNotificationObserver observer( chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY, content::NotificationService::AllSources()); observer.Wait(); } } Browser* GetBrowserNotInSet(std::set<Browser*> excluded_browsers) { for (chrome::BrowserIterator it; !it.done(); it.Next()) { if (excluded_browsers.find(*it) == excluded_browsers.end()) return *it; } return NULL; } namespace { void GetCookiesCallback(base::WaitableEvent* event, std::string* cookies, const std::string& cookie_line) { *cookies = cookie_line; event->Signal(); } void GetCookiesOnIOThread( const GURL& url, const scoped_refptr<net::URLRequestContextGetter>& context_getter, base::WaitableEvent* event, std::string* cookies) { context_getter->GetURLRequestContext()->cookie_store()-> GetCookiesWithOptionsAsync( url, net::CookieOptions(), base::Bind(&GetCookiesCallback, event, cookies)); } } // namespace void GetCookies(const GURL& url, WebContents* contents, int* value_size, std::string* value) { *value_size = -1; if (url.is_valid() && contents) { scoped_refptr<net::URLRequestContextGetter> context_getter = contents->GetBrowserContext()->GetRequestContextForRenderProcess( contents->GetRenderProcessHost()->GetID()); base::WaitableEvent event(true /* manual reset */, false /* not initially signaled */); CHECK(content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, base::Bind(&GetCookiesOnIOThread, url, context_getter, &event, value))); event.Wait(); *value_size = static_cast<int>(value->size()); } } WindowedTabAddedNotificationObserver::WindowedTabAddedNotificationObserver( const content::NotificationSource& source) : WindowedNotificationObserver(chrome::NOTIFICATION_TAB_ADDED, source), added_tab_(NULL) { } void WindowedTabAddedNotificationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { added_tab_ = content::Details<WebContents>(details).ptr(); content::WindowedNotificationObserver::Observe(type, source, details); } UrlLoadObserver::UrlLoadObserver(const GURL& url, const content::NotificationSource& source) : WindowedNotificationObserver(content::NOTIFICATION_LOAD_STOP, source), url_(url) { } UrlLoadObserver::~UrlLoadObserver() {} void UrlLoadObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { NavigationController* controller = content::Source<NavigationController>(source).ptr(); if (controller->GetWebContents()->GetURL() != url_) return; WindowedNotificationObserver::Observe(type, source, details); } BrowserAddedObserver::BrowserAddedObserver() : notification_observer_( chrome::NOTIFICATION_BROWSER_OPENED, content::NotificationService::AllSources()) { for (chrome::BrowserIterator it; !it.done(); it.Next()) original_browsers_.insert(*it); } BrowserAddedObserver::~BrowserAddedObserver() { } Browser* BrowserAddedObserver::WaitForSingleNewBrowser() { notification_observer_.Wait(); // Ensure that only a single new browser has appeared. EXPECT_EQ(original_browsers_.size() + 1, chrome::GetTotalBrowserCount()); return GetBrowserNotInSet(original_browsers_); } #if defined(OS_WIN) bool SaveScreenSnapshotToDirectory(const base::FilePath& directory, base::FilePath* screenshot_path) { bool succeeded = false; base::FilePath out_path(GetSnapshotFileName(directory)); MONITORINFO monitor_info = {}; monitor_info.cbSize = sizeof(monitor_info); HMONITOR main_monitor = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY); if (GetMonitorInfo(main_monitor, &monitor_info)) { RECT& rect = monitor_info.rcMonitor; std::vector<unsigned char> png_data; gfx::Rect bounds( gfx::Size(rect.right - rect.left, rect.bottom - rect.top)); if (ui::GrabDesktopSnapshot(bounds, &png_data) && png_data.size() <= INT_MAX) { int bytes = static_cast<int>(png_data.size()); int written = base::WriteFile( out_path, reinterpret_cast<char*>(&png_data[0]), bytes); succeeded = (written == bytes); } } if (succeeded && screenshot_path != NULL) *screenshot_path = out_path; return succeeded; } bool SaveScreenSnapshotToDesktop(base::FilePath* screenshot_path) { base::FilePath desktop; return PathService::Get(base::DIR_USER_DESKTOP, &desktop) && SaveScreenSnapshotToDirectory(desktop, screenshot_path); } #endif // defined(OS_WIN) void OverrideGeolocation(double latitude, double longitude) { content::Geoposition position; position.latitude = latitude; position.longitude = longitude; position.altitude = 0.; position.accuracy = 0.; position.timestamp = base::Time::Now(); content::GeolocationProvider::GetInstance()->OverrideLocationForTesting( position); } HistoryEnumerator::HistoryEnumerator(Profile* profile) { scoped_refptr<content::MessageLoopRunner> message_loop_runner = new content::MessageLoopRunner; HistoryService* hs = HistoryServiceFactory::GetForProfile( profile, Profile::EXPLICIT_ACCESS); hs->QueryHistory( base::string16(), history::QueryOptions(), &consumer_, base::Bind(&HistoryEnumerator::HistoryQueryComplete, base::Unretained(this), message_loop_runner->QuitClosure())); message_loop_runner->Run(); } HistoryEnumerator::~HistoryEnumerator() {} void HistoryEnumerator::HistoryQueryComplete( const base::Closure& quit_task, HistoryService::Handle request_handle, history::QueryResults* results) { for (size_t i = 0; i < results->size(); ++i) urls_.push_back((*results)[i].url()); quit_task.Run(); } } // namespace ui_test_utils