ui_test_utils.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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/ref_counted.h"
19#include "base/memory/scoped_ptr.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/app_modal_dialogs/app_modal_dialog_queue.h"
40#include "chrome/browser/ui/browser.h"
41#include "chrome/browser/ui/browser_commands.h"
42#include "chrome/browser/ui/browser_finder.h"
43#include "chrome/browser/ui/browser_iterator.h"
44#include "chrome/browser/ui/browser_list.h"
45#include "chrome/browser/ui/browser_navigator.h"
46#include "chrome/browser/ui/browser_window.h"
47#include "chrome/browser/ui/find_bar/find_notification_details.h"
48#include "chrome/browser/ui/find_bar/find_tab_helper.h"
49#include "chrome/browser/ui/host_desktop.h"
50#include "chrome/browser/ui/omnibox/location_bar.h"
51#include "chrome/browser/ui/omnibox/omnibox_view.h"
52#include "chrome/browser/ui/tabs/tab_strip_model.h"
53#include "chrome/common/chrome_paths.h"
54#include "chrome/common/pref_names.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
86using content::BrowserThread;
87using content::DomOperationNotificationDetails;
88using content::NativeWebKeyboardEvent;
89using content::NavigationController;
90using content::NavigationEntry;
91using content::OpenURLParams;
92using content::RenderViewHost;
93using content::RenderWidgetHost;
94using content::Referrer;
95using content::WebContents;
96
97namespace ui_test_utils {
98
99namespace {
100
101#if defined(OS_WIN)
102const char kSnapshotBaseName[] = "ChromiumSnapshot";
103const char kSnapshotExtension[] = ".png";
104
105base::FilePath GetSnapshotFileName(const base::FilePath& snapshot_directory) {
106  base::Time::Exploded the_time;
107
108  base::Time::Now().LocalExplode(&the_time);
109  std::string filename(base::StringPrintf("%s%04d%02d%02d%02d%02d%02d%s",
110      kSnapshotBaseName, the_time.year, the_time.month, the_time.day_of_month,
111      the_time.hour, the_time.minute, the_time.second, kSnapshotExtension));
112
113  base::FilePath snapshot_file = snapshot_directory.AppendASCII(filename);
114  if (base::PathExists(snapshot_file)) {
115    int index = 0;
116    std::string suffix;
117    base::FilePath trial_file;
118    do {
119      suffix = base::StringPrintf(" (%d)", ++index);
120      trial_file = snapshot_file.InsertBeforeExtensionASCII(suffix);
121    } while (base::PathExists(trial_file));
122    snapshot_file = trial_file;
123  }
124  return snapshot_file;
125}
126#endif  // defined(OS_WIN)
127
128Browser* WaitForBrowserNotInSet(std::set<Browser*> excluded_browsers) {
129  Browser* new_browser = GetBrowserNotInSet(excluded_browsers);
130  if (new_browser == NULL) {
131    BrowserAddedObserver observer;
132    new_browser = observer.WaitForSingleNewBrowser();
133    // The new browser should never be in |excluded_browsers|.
134    DCHECK(!ContainsKey(excluded_browsers, new_browser));
135  }
136  return new_browser;
137}
138
139}  // namespace
140
141bool GetCurrentTabTitle(const Browser* browser, string16* title) {
142  WebContents* web_contents =
143      browser->tab_strip_model()->GetActiveWebContents();
144  if (!web_contents)
145    return false;
146  NavigationEntry* last_entry = web_contents->GetController().GetActiveEntry();
147  if (!last_entry)
148    return false;
149  title->assign(last_entry->GetTitleForDisplay(std::string()));
150  return true;
151}
152
153Browser* OpenURLOffTheRecord(Profile* profile, const GURL& url) {
154  chrome::HostDesktopType active_desktop = chrome::GetActiveDesktop();
155  chrome::OpenURLOffTheRecord(profile, url, active_desktop);
156  Browser* browser = chrome::FindTabbedBrowser(
157      profile->GetOffTheRecordProfile(), false, active_desktop);
158  content::TestNavigationObserver observer(
159      browser->tab_strip_model()->GetActiveWebContents());
160  observer.Wait();
161  return browser;
162}
163
164void NavigateToURL(chrome::NavigateParams* params) {
165  chrome::Navigate(params);
166  content::WaitForLoadStop(params->target_contents);
167}
168
169
170void NavigateToURLWithPost(Browser* browser, const GURL& url) {
171  chrome::NavigateParams params(browser, url,
172                                content::PAGE_TRANSITION_FORM_SUBMIT);
173  params.uses_post = true;
174  NavigateToURL(&params);
175}
176
177void 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.
186static 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    same_tab_observer.Wait();
235    return;
236  } else if (web_contents) {
237    content::TestNavigationObserver observer(web_contents,
238                                             number_of_navigations);
239    observer.Wait();
240    return;
241  }
242  EXPECT_TRUE(NULL != web_contents) << " Unable to wait for navigation to \""
243                                    << url.spec() << "\""
244                                    << " because we can't get the tab contents";
245}
246
247void NavigateToURLWithDisposition(Browser* browser,
248                                  const GURL& url,
249                                  WindowOpenDisposition disposition,
250                                  int browser_test_flags) {
251  NavigateToURLWithDispositionBlockUntilNavigationsComplete(
252      browser,
253      url,
254      1,
255      disposition,
256      browser_test_flags);
257}
258
259void NavigateToURLBlockUntilNavigationsComplete(Browser* browser,
260                                                const GURL& url,
261                                                int number_of_navigations) {
262  NavigateToURLWithDispositionBlockUntilNavigationsComplete(
263      browser,
264      url,
265      number_of_navigations,
266      CURRENT_TAB,
267      BROWSER_TEST_WAIT_FOR_NAVIGATION);
268}
269
270base::FilePath GetTestFilePath(const base::FilePath& dir,
271                               const base::FilePath& file) {
272  base::FilePath path;
273  PathService::Get(chrome::DIR_TEST_DATA, &path);
274  return path.Append(dir).Append(file);
275}
276
277GURL GetTestUrl(const base::FilePath& dir, const base::FilePath& file) {
278  return net::FilePathToFileURL(GetTestFilePath(dir, file));
279}
280
281bool GetRelativeBuildDirectory(base::FilePath* build_dir) {
282  // This function is used to find the build directory so TestServer can serve
283  // built files (nexes, etc).  TestServer expects a path relative to the source
284  // root.
285  base::FilePath exe_dir =
286      CommandLine::ForCurrentProcess()->GetProgram().DirName();
287  base::FilePath src_dir;
288  if (!PathService::Get(base::DIR_SOURCE_ROOT, &src_dir))
289    return false;
290
291  // We must first generate absolute paths to SRC and EXE and from there
292  // generate a relative path.
293  if (!exe_dir.IsAbsolute())
294    exe_dir = base::MakeAbsoluteFilePath(exe_dir);
295  if (!src_dir.IsAbsolute())
296    src_dir = base::MakeAbsoluteFilePath(src_dir);
297  if (!exe_dir.IsAbsolute())
298    return false;
299  if (!src_dir.IsAbsolute())
300    return false;
301
302  size_t match, exe_size, src_size;
303  std::vector<base::FilePath::StringType> src_parts, exe_parts;
304
305  // Determine point at which src and exe diverge.
306  exe_dir.GetComponents(&exe_parts);
307  src_dir.GetComponents(&src_parts);
308  exe_size = exe_parts.size();
309  src_size = src_parts.size();
310  for (match = 0; match < exe_size && match < src_size; ++match) {
311    if (exe_parts[match] != src_parts[match])
312      break;
313  }
314
315  // Create a relative path.
316  *build_dir = base::FilePath();
317  for (size_t tmp_itr = match; tmp_itr < src_size; ++tmp_itr)
318    *build_dir = build_dir->Append(FILE_PATH_LITERAL(".."));
319  for (; match < exe_size; ++match)
320    *build_dir = build_dir->Append(exe_parts[match]);
321  return true;
322}
323
324AppModalDialog* WaitForAppModalDialog() {
325  AppModalDialogQueue* dialog_queue = AppModalDialogQueue::GetInstance();
326  if (dialog_queue->HasActiveDialog())
327    return dialog_queue->active_dialog();
328
329  content::WindowedNotificationObserver observer(
330      chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN,
331      content::NotificationService::AllSources());
332  observer.Wait();
333  return content::Source<AppModalDialog>(observer.source()).ptr();
334}
335
336int FindInPage(WebContents* tab, const string16& search_string,
337               bool forward, bool match_case, int* ordinal,
338               gfx::Rect* selection_rect) {
339  FindTabHelper* find_tab_helper = FindTabHelper::FromWebContents(tab);
340  find_tab_helper->StartFinding(search_string, forward, match_case);
341  FindInPageNotificationObserver observer(tab);
342  observer.Wait();
343  if (ordinal)
344    *ordinal = observer.active_match_ordinal();
345  if (selection_rect)
346    *selection_rect = observer.selection_rect();
347  return observer.number_of_matches();
348}
349
350void WaitForTemplateURLServiceToLoad(TemplateURLService* service) {
351  if (service->loaded())
352    return;
353  scoped_refptr<content::MessageLoopRunner> message_loop_runner =
354      new content::MessageLoopRunner;
355  scoped_ptr<TemplateURLService::Subscription> subscription =
356      service->RegisterOnLoadedCallback(
357          message_loop_runner->QuitClosure());
358  service->Load();
359  message_loop_runner->Run();
360
361  ASSERT_TRUE(service->loaded());
362}
363
364void WaitForHistoryToLoad(HistoryService* history_service) {
365  content::WindowedNotificationObserver history_loaded_observer(
366      chrome::NOTIFICATION_HISTORY_LOADED,
367      content::NotificationService::AllSources());
368  if (!history_service->BackendLoaded())
369    history_loaded_observer.Wait();
370}
371
372void DownloadURL(Browser* browser, const GURL& download_url) {
373  base::ScopedTempDir downloads_directory;
374  ASSERT_TRUE(downloads_directory.CreateUniqueTempDir());
375  browser->profile()->GetPrefs()->SetFilePath(
376      prefs::kDownloadDefaultDirectory, downloads_directory.path());
377
378  content::DownloadManager* download_manager =
379      content::BrowserContext::GetDownloadManager(browser->profile());
380  scoped_ptr<content::DownloadTestObserver> observer(
381      new content::DownloadTestObserverTerminal(
382          download_manager, 1,
383          content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT));
384
385  ui_test_utils::NavigateToURL(browser, download_url);
386  observer->WaitForFinished();
387}
388
389void SendToOmniboxAndSubmit(LocationBar* location_bar,
390                            const std::string& input) {
391  OmniboxView* omnibox = location_bar->GetOmniboxView();
392  omnibox->model()->OnSetFocus(false);
393  omnibox->SetUserText(ASCIIToUTF16(input));
394  location_bar->AcceptInput();
395  while (!omnibox->model()->autocomplete_controller()->done()) {
396    content::WindowedNotificationObserver observer(
397        chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
398        content::NotificationService::AllSources());
399    observer.Wait();
400  }
401}
402
403Browser* GetBrowserNotInSet(std::set<Browser*> excluded_browsers) {
404  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
405    if (excluded_browsers.find(*it) == excluded_browsers.end())
406      return *it;
407  }
408  return NULL;
409}
410
411WindowedTabAddedNotificationObserver::WindowedTabAddedNotificationObserver(
412    const content::NotificationSource& source)
413    : WindowedNotificationObserver(chrome::NOTIFICATION_TAB_ADDED, source),
414      added_tab_(NULL) {
415}
416
417void WindowedTabAddedNotificationObserver::Observe(
418    int type,
419    const content::NotificationSource& source,
420    const content::NotificationDetails& details) {
421  added_tab_ = content::Details<WebContents>(details).ptr();
422  content::WindowedNotificationObserver::Observe(type, source, details);
423}
424
425UrlLoadObserver::UrlLoadObserver(const GURL& url,
426                                 const content::NotificationSource& source)
427    : WindowedNotificationObserver(content::NOTIFICATION_LOAD_STOP, source),
428      url_(url) {
429}
430
431UrlLoadObserver::~UrlLoadObserver() {}
432
433void UrlLoadObserver::Observe(
434    int type,
435    const content::NotificationSource& source,
436    const content::NotificationDetails& details) {
437  NavigationController* controller =
438      content::Source<NavigationController>(source).ptr();
439  if (controller->GetWebContents()->GetURL() != url_)
440    return;
441
442  WindowedNotificationObserver::Observe(type, source, details);
443}
444
445BrowserAddedObserver::BrowserAddedObserver()
446    : notification_observer_(
447          chrome::NOTIFICATION_BROWSER_OPENED,
448          content::NotificationService::AllSources()) {
449  for (chrome::BrowserIterator it; !it.done(); it.Next())
450    original_browsers_.insert(*it);
451}
452
453BrowserAddedObserver::~BrowserAddedObserver() {
454}
455
456Browser* BrowserAddedObserver::WaitForSingleNewBrowser() {
457  notification_observer_.Wait();
458  // Ensure that only a single new browser has appeared.
459  EXPECT_EQ(original_browsers_.size() + 1, chrome::GetTotalBrowserCount());
460  return GetBrowserNotInSet(original_browsers_);
461}
462
463#if defined(OS_WIN)
464
465bool SaveScreenSnapshotToDirectory(const base::FilePath& directory,
466                                   base::FilePath* screenshot_path) {
467  bool succeeded = false;
468  base::FilePath out_path(GetSnapshotFileName(directory));
469
470  MONITORINFO monitor_info = {};
471  monitor_info.cbSize = sizeof(monitor_info);
472  HMONITOR main_monitor = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY);
473  if (GetMonitorInfo(main_monitor, &monitor_info)) {
474    RECT& rect = monitor_info.rcMonitor;
475
476    std::vector<unsigned char> png_data;
477    gfx::Rect bounds(
478        gfx::Size(rect.right - rect.left, rect.bottom - rect.top));
479    if (ui::GrabDesktopSnapshot(bounds, &png_data) &&
480        png_data.size() <= INT_MAX) {
481      int bytes = static_cast<int>(png_data.size());
482      int written = file_util::WriteFile(
483          out_path, reinterpret_cast<char*>(&png_data[0]), bytes);
484      succeeded = (written == bytes);
485    }
486  }
487
488  if (succeeded && screenshot_path != NULL)
489    *screenshot_path = out_path;
490
491  return succeeded;
492}
493
494bool SaveScreenSnapshotToDesktop(base::FilePath* screenshot_path) {
495  base::FilePath desktop;
496
497  return PathService::Get(base::DIR_USER_DESKTOP, &desktop) &&
498      SaveScreenSnapshotToDirectory(desktop, screenshot_path);
499}
500
501#endif  // defined(OS_WIN)
502
503void OverrideGeolocation(double latitude, double longitude) {
504  content::Geoposition position;
505  position.latitude = latitude;
506  position.longitude = longitude;
507  position.altitude = 0.;
508  position.accuracy = 0.;
509  position.timestamp = base::Time::Now();
510  scoped_refptr<content::MessageLoopRunner> runner =
511      new content::MessageLoopRunner;
512
513  content::GeolocationProvider::OverrideLocationForTesting(
514      position, runner->QuitClosure());
515
516  runner->Run();
517}
518
519HistoryEnumerator::HistoryEnumerator(Profile* profile) {
520  scoped_refptr<content::MessageLoopRunner> message_loop_runner =
521      new content::MessageLoopRunner;
522
523  HistoryService* hs = HistoryServiceFactory::GetForProfile(
524      profile, Profile::EXPLICIT_ACCESS);
525  hs->QueryHistory(
526      string16(),
527      history::QueryOptions(),
528      &consumer_,
529      base::Bind(&HistoryEnumerator::HistoryQueryComplete,
530                 base::Unretained(this), message_loop_runner->QuitClosure()));
531  message_loop_runner->Run();
532}
533
534HistoryEnumerator::~HistoryEnumerator() {}
535
536void HistoryEnumerator::HistoryQueryComplete(
537    const base::Closure& quit_task,
538    HistoryService::Handle request_handle,
539    history::QueryResults* results) {
540  for (size_t i = 0; i < results->size(); ++i)
541    urls_.push_back((*results)[i].url());
542  quit_task.Run();
543}
544
545}  // namespace ui_test_utils
546