1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/test/base/ui_test_utils.h" 6 7 #if defined(OS_WIN) 8 #include <windows.h> 9 #endif 10 11 #include "base/bind.h" 12 #include "base/bind_helpers.h" 13 #include "base/callback.h" 14 #include "base/command_line.h" 15 #include "base/file_util.h" 16 #include "base/files/file_path.h" 17 #include "base/json/json_reader.h" 18 #include "base/memory/scoped_ptr.h" 19 #include "base/message_loop/message_loop.h" 20 #include "base/path_service.h" 21 #include "base/prefs/pref_service.h" 22 #include "base/strings/stringprintf.h" 23 #include "base/strings/utf_string_conversions.h" 24 #include "base/test/test_timeouts.h" 25 #include "base/time/time.h" 26 #include "base/values.h" 27 #include "chrome/browser/autocomplete/autocomplete_controller.h" 28 #include "chrome/browser/bookmarks/bookmark_model.h" 29 #include "chrome/browser/bookmarks/bookmark_model_factory.h" 30 #include "chrome/browser/browser_process.h" 31 #include "chrome/browser/chrome_notification_types.h" 32 #include "chrome/browser/extensions/extension_action.h" 33 #include "chrome/browser/history/history_service_factory.h" 34 #include "chrome/browser/profiles/profile.h" 35 #include "chrome/browser/search_engines/template_url_service.h" 36 #include "chrome/browser/search_engines/template_url_service_test_util.h" 37 #include "chrome/browser/thumbnails/render_widget_snapshot_taker.h" 38 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog.h" 39 #include "chrome/browser/ui/browser.h" 40 #include "chrome/browser/ui/browser_commands.h" 41 #include "chrome/browser/ui/browser_finder.h" 42 #include "chrome/browser/ui/browser_iterator.h" 43 #include "chrome/browser/ui/browser_list.h" 44 #include "chrome/browser/ui/browser_navigator.h" 45 #include "chrome/browser/ui/browser_window.h" 46 #include "chrome/browser/ui/find_bar/find_notification_details.h" 47 #include "chrome/browser/ui/find_bar/find_tab_helper.h" 48 #include "chrome/browser/ui/host_desktop.h" 49 #include "chrome/browser/ui/omnibox/location_bar.h" 50 #include "chrome/browser/ui/omnibox/omnibox_view.h" 51 #include "chrome/browser/ui/tabs/tab_strip_model.h" 52 #include "chrome/common/chrome_paths.h" 53 #include "chrome/common/pref_names.h" 54 #include "chrome/test/base/bookmark_load_observer.h" 55 #include "chrome/test/base/find_in_page_observer.h" 56 #include "content/public/browser/browser_thread.h" 57 #include "content/public/browser/dom_operation_notification_details.h" 58 #include "content/public/browser/download_item.h" 59 #include "content/public/browser/download_manager.h" 60 #include "content/public/browser/geolocation_provider.h" 61 #include "content/public/browser/navigation_controller.h" 62 #include "content/public/browser/navigation_entry.h" 63 #include "content/public/browser/notification_service.h" 64 #include "content/public/browser/render_process_host.h" 65 #include "content/public/browser/render_view_host.h" 66 #include "content/public/browser/web_contents.h" 67 #include "content/public/browser/web_contents_observer.h" 68 #include "content/public/browser/web_contents_view.h" 69 #include "content/public/common/geoposition.h" 70 #include "content/public/test/browser_test_utils.h" 71 #include "content/public/test/download_test_observer.h" 72 #include "content/public/test/test_navigation_observer.h" 73 #include "content/public/test/test_utils.h" 74 #include "net/base/net_util.h" 75 #include "net/test/python_utils.h" 76 #include "third_party/skia/include/core/SkBitmap.h" 77 #include "third_party/skia/include/core/SkColor.h" 78 #include "ui/gfx/size.h" 79 #include "ui/snapshot/test/snapshot_desktop.h" 80 81 #if defined(USE_AURA) 82 #include "ash/shell.h" 83 #include "ui/aura/root_window.h" 84 #endif 85 86 using content::BrowserThread; 87 using content::DomOperationNotificationDetails; 88 using content::NativeWebKeyboardEvent; 89 using content::NavigationController; 90 using content::NavigationEntry; 91 using content::OpenURLParams; 92 using content::RenderViewHost; 93 using content::RenderWidgetHost; 94 using content::Referrer; 95 using content::WebContents; 96 97 namespace ui_test_utils { 98 99 namespace { 100 101 const char kSnapshotBaseName[] = "ChromiumSnapshot"; 102 const char kSnapshotExtension[] = ".png"; 103 104 base::FilePath GetSnapshotFileName(const base::FilePath& snapshot_directory) { 105 base::Time::Exploded the_time; 106 107 base::Time::Now().LocalExplode(&the_time); 108 std::string filename(base::StringPrintf("%s%04d%02d%02d%02d%02d%02d%s", 109 kSnapshotBaseName, the_time.year, the_time.month, the_time.day_of_month, 110 the_time.hour, the_time.minute, the_time.second, kSnapshotExtension)); 111 112 base::FilePath snapshot_file = snapshot_directory.AppendASCII(filename); 113 if (base::PathExists(snapshot_file)) { 114 int index = 0; 115 std::string suffix; 116 base::FilePath trial_file; 117 do { 118 suffix = base::StringPrintf(" (%d)", ++index); 119 trial_file = snapshot_file.InsertBeforeExtensionASCII(suffix); 120 } while (base::PathExists(trial_file)); 121 snapshot_file = trial_file; 122 } 123 return snapshot_file; 124 } 125 126 } // namespace 127 128 bool GetCurrentTabTitle(const Browser* browser, string16* title) { 129 WebContents* web_contents = 130 browser->tab_strip_model()->GetActiveWebContents(); 131 if (!web_contents) 132 return false; 133 NavigationEntry* last_entry = web_contents->GetController().GetActiveEntry(); 134 if (!last_entry) 135 return false; 136 title->assign(last_entry->GetTitleForDisplay(std::string())); 137 return true; 138 } 139 140 void WaitForNavigations(NavigationController* controller, 141 int number_of_navigations) { 142 content::TestNavigationObserver observer(controller->GetWebContents(), 143 number_of_navigations); 144 base::RunLoop run_loop; 145 observer.WaitForObservation( 146 base::Bind(&content::RunThisRunLoop, base::Unretained(&run_loop)), 147 content::GetQuitTaskForRunLoop(&run_loop)); 148 } 149 150 Browser* WaitForBrowserNotInSet(std::set<Browser*> excluded_browsers) { 151 Browser* new_browser = GetBrowserNotInSet(excluded_browsers); 152 if (new_browser == NULL) { 153 BrowserAddedObserver observer; 154 new_browser = observer.WaitForSingleNewBrowser(); 155 // The new browser should never be in |excluded_browsers|. 156 DCHECK(!ContainsKey(excluded_browsers, new_browser)); 157 } 158 return new_browser; 159 } 160 161 Browser* OpenURLOffTheRecord(Profile* profile, const GURL& url) { 162 chrome::HostDesktopType active_desktop = chrome::GetActiveDesktop(); 163 chrome::OpenURLOffTheRecord(profile, url, active_desktop); 164 Browser* browser = chrome::FindTabbedBrowser( 165 profile->GetOffTheRecordProfile(), false, active_desktop); 166 WaitForNavigations( 167 &browser->tab_strip_model()->GetActiveWebContents()->GetController(), 168 1); 169 return browser; 170 } 171 172 void NavigateToURL(chrome::NavigateParams* params) { 173 chrome::Navigate(params); 174 content::WaitForLoadStop(params->target_contents); 175 } 176 177 void NavigateToURL(Browser* browser, const GURL& url) { 178 NavigateToURLWithDisposition(browser, url, CURRENT_TAB, 179 BROWSER_TEST_WAIT_FOR_NAVIGATION); 180 } 181 182 // Navigates the specified tab (via |disposition|) of |browser| to |url|, 183 // blocking until the |number_of_navigations| specified complete. 184 // |disposition| indicates what tab the download occurs in, and 185 // |browser_test_flags| controls what to wait for before continuing. 186 static void NavigateToURLWithDispositionBlockUntilNavigationsComplete( 187 Browser* browser, 188 const GURL& url, 189 int number_of_navigations, 190 WindowOpenDisposition disposition, 191 int browser_test_flags) { 192 TabStripModel* tab_strip = browser->tab_strip_model(); 193 if (disposition == CURRENT_TAB && tab_strip->GetActiveWebContents()) 194 content::WaitForLoadStop(tab_strip->GetActiveWebContents()); 195 content::TestNavigationObserver same_tab_observer( 196 tab_strip->GetActiveWebContents(), 197 number_of_navigations); 198 199 std::set<Browser*> initial_browsers; 200 for (chrome::BrowserIterator it; !it.done(); it.Next()) 201 initial_browsers.insert(*it); 202 203 content::WindowedNotificationObserver tab_added_observer( 204 chrome::NOTIFICATION_TAB_ADDED, 205 content::NotificationService::AllSources()); 206 207 browser->OpenURL(OpenURLParams( 208 url, Referrer(), disposition, content::PAGE_TRANSITION_TYPED, false)); 209 if (browser_test_flags & BROWSER_TEST_WAIT_FOR_BROWSER) 210 browser = WaitForBrowserNotInSet(initial_browsers); 211 if (browser_test_flags & BROWSER_TEST_WAIT_FOR_TAB) 212 tab_added_observer.Wait(); 213 if (!(browser_test_flags & BROWSER_TEST_WAIT_FOR_NAVIGATION)) { 214 // Some other flag caused the wait prior to this. 215 return; 216 } 217 WebContents* web_contents = NULL; 218 if (disposition == NEW_BACKGROUND_TAB) { 219 // We've opened up a new tab, but not selected it. 220 TabStripModel* tab_strip = browser->tab_strip_model(); 221 web_contents = tab_strip->GetWebContentsAt(tab_strip->active_index() + 1); 222 EXPECT_TRUE(web_contents != NULL) 223 << " Unable to wait for navigation to \"" << url.spec() 224 << "\" because the new tab is not available yet"; 225 if (!web_contents) 226 return; 227 } else if ((disposition == CURRENT_TAB) || 228 (disposition == NEW_FOREGROUND_TAB) || 229 (disposition == SINGLETON_TAB)) { 230 // The currently selected tab is the right one. 231 web_contents = browser->tab_strip_model()->GetActiveWebContents(); 232 } 233 if (disposition == CURRENT_TAB) { 234 base::RunLoop run_loop; 235 same_tab_observer.WaitForObservation( 236 base::Bind(&content::RunThisRunLoop, base::Unretained(&run_loop)), 237 content::GetQuitTaskForRunLoop(&run_loop)); 238 return; 239 } else if (web_contents) { 240 NavigationController* controller = &web_contents->GetController(); 241 WaitForNavigations(controller, number_of_navigations); 242 return; 243 } 244 EXPECT_TRUE(NULL != web_contents) << " Unable to wait for navigation to \"" 245 << url.spec() << "\"" 246 << " because we can't get the tab contents"; 247 } 248 249 void NavigateToURLWithDisposition(Browser* browser, 250 const GURL& url, 251 WindowOpenDisposition disposition, 252 int browser_test_flags) { 253 NavigateToURLWithDispositionBlockUntilNavigationsComplete( 254 browser, 255 url, 256 1, 257 disposition, 258 browser_test_flags); 259 } 260 261 void NavigateToURLBlockUntilNavigationsComplete(Browser* browser, 262 const GURL& url, 263 int number_of_navigations) { 264 NavigateToURLWithDispositionBlockUntilNavigationsComplete( 265 browser, 266 url, 267 number_of_navigations, 268 CURRENT_TAB, 269 BROWSER_TEST_WAIT_FOR_NAVIGATION); 270 } 271 272 base::FilePath GetTestFilePath(const base::FilePath& dir, 273 const base::FilePath& file) { 274 base::FilePath path; 275 PathService::Get(chrome::DIR_TEST_DATA, &path); 276 return path.Append(dir).Append(file); 277 } 278 279 GURL GetTestUrl(const base::FilePath& dir, const base::FilePath& file) { 280 return net::FilePathToFileURL(GetTestFilePath(dir, file)); 281 } 282 283 bool GetRelativeBuildDirectory(base::FilePath* build_dir) { 284 // This function is used to find the build directory so TestServer can serve 285 // built files (nexes, etc). TestServer expects a path relative to the source 286 // root. 287 base::FilePath exe_dir = 288 CommandLine::ForCurrentProcess()->GetProgram().DirName(); 289 base::FilePath src_dir; 290 if (!PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)) 291 return false; 292 293 // We must first generate absolute paths to SRC and EXE and from there 294 // generate a relative path. 295 if (!exe_dir.IsAbsolute()) 296 exe_dir = base::MakeAbsoluteFilePath(exe_dir); 297 if (!src_dir.IsAbsolute()) 298 src_dir = base::MakeAbsoluteFilePath(src_dir); 299 if (!exe_dir.IsAbsolute()) 300 return false; 301 if (!src_dir.IsAbsolute()) 302 return false; 303 304 size_t match, exe_size, src_size; 305 std::vector<base::FilePath::StringType> src_parts, exe_parts; 306 307 // Determine point at which src and exe diverge. 308 exe_dir.GetComponents(&exe_parts); 309 src_dir.GetComponents(&src_parts); 310 exe_size = exe_parts.size(); 311 src_size = src_parts.size(); 312 for (match = 0; match < exe_size && match < src_size; ++match) { 313 if (exe_parts[match] != src_parts[match]) 314 break; 315 } 316 317 // Create a relative path. 318 *build_dir = base::FilePath(); 319 for (size_t tmp_itr = match; tmp_itr < src_size; ++tmp_itr) 320 *build_dir = build_dir->Append(FILE_PATH_LITERAL("..")); 321 for (; match < exe_size; ++match) 322 *build_dir = build_dir->Append(exe_parts[match]); 323 return true; 324 } 325 326 AppModalDialog* WaitForAppModalDialog() { 327 content::WindowedNotificationObserver observer( 328 chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN, 329 content::NotificationService::AllSources()); 330 observer.Wait(); 331 return content::Source<AppModalDialog>(observer.source()).ptr(); 332 } 333 334 int FindInPage(WebContents* tab, const string16& search_string, 335 bool forward, bool match_case, int* ordinal, 336 gfx::Rect* selection_rect) { 337 FindTabHelper* find_tab_helper = FindTabHelper::FromWebContents(tab); 338 find_tab_helper->StartFinding(search_string, forward, match_case); 339 FindInPageNotificationObserver observer(tab); 340 observer.Wait(); 341 if (ordinal) 342 *ordinal = observer.active_match_ordinal(); 343 if (selection_rect) 344 *selection_rect = observer.selection_rect(); 345 return observer.number_of_matches(); 346 } 347 348 void RegisterAndWait(content::NotificationObserver* observer, 349 int type, 350 const content::NotificationSource& source) { 351 content::NotificationRegistrar registrar; 352 registrar.Add(observer, type, source); 353 content::RunMessageLoop(); 354 } 355 356 void WaitForBookmarkModelToLoad(BookmarkModel* model) { 357 if (model->loaded()) 358 return; 359 base::RunLoop run_loop; 360 BookmarkLoadObserver observer(content::GetQuitTaskForRunLoop(&run_loop)); 361 model->AddObserver(&observer); 362 content::RunThisRunLoop(&run_loop); 363 model->RemoveObserver(&observer); 364 ASSERT_TRUE(model->loaded()); 365 } 366 367 void WaitForBookmarkModelToLoad(Profile* profile) { 368 WaitForBookmarkModelToLoad(BookmarkModelFactory::GetForProfile(profile)); 369 } 370 371 void WaitForTemplateURLServiceToLoad(TemplateURLService* service) { 372 if (service->loaded()) 373 return; 374 375 content::WindowedNotificationObserver observer( 376 chrome::NOTIFICATION_TEMPLATE_URL_SERVICE_LOADED, 377 content::Source<TemplateURLService>(service)); 378 service->Load(); 379 observer.Wait(); 380 381 ASSERT_TRUE(service->loaded()); 382 } 383 384 void WaitForHistoryToLoad(HistoryService* history_service) { 385 content::WindowedNotificationObserver history_loaded_observer( 386 chrome::NOTIFICATION_HISTORY_LOADED, 387 content::NotificationService::AllSources()); 388 if (!history_service->BackendLoaded()) 389 history_loaded_observer.Wait(); 390 } 391 392 void DownloadURL(Browser* browser, const GURL& download_url) { 393 base::ScopedTempDir downloads_directory; 394 ASSERT_TRUE(downloads_directory.CreateUniqueTempDir()); 395 browser->profile()->GetPrefs()->SetFilePath( 396 prefs::kDownloadDefaultDirectory, downloads_directory.path()); 397 398 content::DownloadManager* download_manager = 399 content::BrowserContext::GetDownloadManager(browser->profile()); 400 scoped_ptr<content::DownloadTestObserver> observer( 401 new content::DownloadTestObserverTerminal( 402 download_manager, 1, 403 content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT)); 404 405 ui_test_utils::NavigateToURL(browser, download_url); 406 observer->WaitForFinished(); 407 } 408 409 void SendToOmniboxAndSubmit(LocationBar* location_bar, 410 const std::string& input) { 411 OmniboxView* omnibox = location_bar->GetLocationEntry(); 412 omnibox->model()->OnSetFocus(false); 413 omnibox->SetUserText(ASCIIToUTF16(input)); 414 location_bar->AcceptInput(); 415 while (!omnibox->model()->autocomplete_controller()->done()) { 416 content::WindowedNotificationObserver observer( 417 chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY, 418 content::NotificationService::AllSources()); 419 observer.Wait(); 420 } 421 } 422 423 Browser* GetBrowserNotInSet(std::set<Browser*> excluded_browsers) { 424 for (chrome::BrowserIterator it; !it.done(); it.Next()) { 425 if (excluded_browsers.find(*it) == excluded_browsers.end()) 426 return *it; 427 } 428 return NULL; 429 } 430 431 WindowedTabAddedNotificationObserver::WindowedTabAddedNotificationObserver( 432 const content::NotificationSource& source) 433 : WindowedNotificationObserver(chrome::NOTIFICATION_TAB_ADDED, source), 434 added_tab_(NULL) { 435 } 436 437 void WindowedTabAddedNotificationObserver::Observe( 438 int type, 439 const content::NotificationSource& source, 440 const content::NotificationDetails& details) { 441 added_tab_ = content::Details<WebContents>(details).ptr(); 442 content::WindowedNotificationObserver::Observe(type, source, details); 443 } 444 445 UrlLoadObserver::UrlLoadObserver(const GURL& url, 446 const content::NotificationSource& source) 447 : WindowedNotificationObserver(content::NOTIFICATION_LOAD_STOP, source), 448 url_(url) { 449 } 450 451 UrlLoadObserver::~UrlLoadObserver() {} 452 453 void UrlLoadObserver::Observe( 454 int type, 455 const content::NotificationSource& source, 456 const content::NotificationDetails& details) { 457 NavigationController* controller = 458 content::Source<NavigationController>(source).ptr(); 459 if (controller->GetWebContents()->GetURL() != url_) 460 return; 461 462 WindowedNotificationObserver::Observe(type, source, details); 463 } 464 465 BrowserAddedObserver::BrowserAddedObserver() 466 : notification_observer_( 467 chrome::NOTIFICATION_BROWSER_OPENED, 468 content::NotificationService::AllSources()) { 469 for (chrome::BrowserIterator it; !it.done(); it.Next()) 470 original_browsers_.insert(*it); 471 } 472 473 BrowserAddedObserver::~BrowserAddedObserver() { 474 } 475 476 Browser* BrowserAddedObserver::WaitForSingleNewBrowser() { 477 notification_observer_.Wait(); 478 // Ensure that only a single new browser has appeared. 479 EXPECT_EQ(original_browsers_.size() + 1, chrome::GetTotalBrowserCount()); 480 return GetBrowserNotInSet(original_browsers_); 481 } 482 483 #if defined(OS_WIN) 484 485 bool SaveScreenSnapshotToDirectory(const base::FilePath& directory, 486 base::FilePath* screenshot_path) { 487 bool succeeded = false; 488 base::FilePath out_path(GetSnapshotFileName(directory)); 489 490 MONITORINFO monitor_info = {}; 491 monitor_info.cbSize = sizeof(monitor_info); 492 HMONITOR main_monitor = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY); 493 if (GetMonitorInfo(main_monitor, &monitor_info)) { 494 RECT& rect = monitor_info.rcMonitor; 495 496 std::vector<unsigned char> png_data; 497 gfx::Rect bounds( 498 gfx::Size(rect.right - rect.left, rect.bottom - rect.top)); 499 if (ui::GrabDesktopSnapshot(bounds, &png_data) && 500 png_data.size() <= INT_MAX) { 501 int bytes = static_cast<int>(png_data.size()); 502 int written = file_util::WriteFile( 503 out_path, reinterpret_cast<char*>(&png_data[0]), bytes); 504 succeeded = (written == bytes); 505 } 506 } 507 508 if (succeeded && screenshot_path != NULL) 509 *screenshot_path = out_path; 510 511 return succeeded; 512 } 513 514 bool SaveScreenSnapshotToDesktop(base::FilePath* screenshot_path) { 515 base::FilePath desktop; 516 517 return PathService::Get(base::DIR_USER_DESKTOP, &desktop) && 518 SaveScreenSnapshotToDirectory(desktop, screenshot_path); 519 } 520 521 #endif // defined(OS_WIN) 522 523 void OverrideGeolocation(double latitude, double longitude) { 524 content::Geoposition position; 525 position.latitude = latitude; 526 position.longitude = longitude; 527 position.altitude = 0.; 528 position.accuracy = 0.; 529 position.timestamp = base::Time::Now(); 530 scoped_refptr<content::MessageLoopRunner> runner = 531 new content::MessageLoopRunner; 532 533 content::GeolocationProvider::OverrideLocationForTesting( 534 position, runner->QuitClosure()); 535 536 runner->Run(); 537 } 538 539 HistoryEnumerator::HistoryEnumerator(Profile* profile) { 540 scoped_refptr<content::MessageLoopRunner> message_loop_runner = 541 new content::MessageLoopRunner; 542 543 HistoryService* hs = HistoryServiceFactory::GetForProfile( 544 profile, Profile::EXPLICIT_ACCESS); 545 hs->QueryHistory( 546 string16(), 547 history::QueryOptions(), 548 &consumer_, 549 base::Bind(&HistoryEnumerator::HistoryQueryComplete, 550 base::Unretained(this), message_loop_runner->QuitClosure())); 551 message_loop_runner->Run(); 552 } 553 554 HistoryEnumerator::~HistoryEnumerator() {} 555 556 void HistoryEnumerator::HistoryQueryComplete( 557 const base::Closure& quit_task, 558 HistoryService::Handle request_handle, 559 history::QueryResults* results) { 560 for (size_t i = 0; i < results->size(); ++i) 561 urls_.push_back((*results)[i].url()); 562 quit_task.Run(); 563 } 564 565 } // namespace ui_test_utils 566