ui_test_utils.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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_factory.h"
29#include "chrome/browser/browser_process.h"
30#include "chrome/browser/chrome_notification_types.h"
31#include "chrome/browser/devtools/devtools_window.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/ui/app_modal_dialogs/app_modal_dialog.h"
38#include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.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/find_in_page_observer.h"
55#include "components/bookmarks/browser/bookmark_model.h"
56#include "content/public/browser/dom_operation_notification_details.h"
57#include "content/public/browser/download_item.h"
58#include "content/public/browser/download_manager.h"
59#include "content/public/browser/geolocation_provider.h"
60#include "content/public/browser/navigation_controller.h"
61#include "content/public/browser/navigation_entry.h"
62#include "content/public/browser/notification_service.h"
63#include "content/public/browser/render_process_host.h"
64#include "content/public/browser/render_view_host.h"
65#include "content/public/browser/web_contents.h"
66#include "content/public/browser/web_contents_observer.h"
67#include "content/public/common/geoposition.h"
68#include "content/public/test/browser_test_utils.h"
69#include "content/public/test/download_test_observer.h"
70#include "content/public/test/test_navigation_observer.h"
71#include "content/public/test/test_utils.h"
72#include "net/base/filename_util.h"
73#include "net/cookies/cookie_constants.h"
74#include "net/cookies/cookie_monster.h"
75#include "net/cookies/cookie_store.h"
76#include "net/test/python_utils.h"
77#include "net/url_request/url_request_context.h"
78#include "net/url_request/url_request_context_getter.h"
79#include "third_party/skia/include/core/SkBitmap.h"
80#include "third_party/skia/include/core/SkColor.h"
81#include "ui/gfx/size.h"
82#include "ui/snapshot/test/snapshot_desktop.h"
83
84#if defined(USE_AURA)
85#include "ash/shell.h"
86#include "ui/aura/window_event_dispatcher.h"
87#endif
88
89using content::DomOperationNotificationDetails;
90using content::NativeWebKeyboardEvent;
91using content::NavigationController;
92using content::NavigationEntry;
93using content::OpenURLParams;
94using content::RenderViewHost;
95using content::RenderWidgetHost;
96using content::Referrer;
97using content::WebContents;
98
99namespace ui_test_utils {
100
101namespace {
102
103#if defined(OS_WIN)
104const char kSnapshotBaseName[] = "ChromiumSnapshot";
105const char kSnapshotExtension[] = ".png";
106
107base::FilePath GetSnapshotFileName(const base::FilePath& snapshot_directory) {
108  base::Time::Exploded the_time;
109
110  base::Time::Now().LocalExplode(&the_time);
111  std::string filename(base::StringPrintf("%s%04d%02d%02d%02d%02d%02d%s",
112      kSnapshotBaseName, the_time.year, the_time.month, the_time.day_of_month,
113      the_time.hour, the_time.minute, the_time.second, kSnapshotExtension));
114
115  base::FilePath snapshot_file = snapshot_directory.AppendASCII(filename);
116  if (base::PathExists(snapshot_file)) {
117    int index = 0;
118    std::string suffix;
119    base::FilePath trial_file;
120    do {
121      suffix = base::StringPrintf(" (%d)", ++index);
122      trial_file = snapshot_file.InsertBeforeExtensionASCII(suffix);
123    } while (base::PathExists(trial_file));
124    snapshot_file = trial_file;
125  }
126  return snapshot_file;
127}
128#endif  // defined(OS_WIN)
129
130Browser* WaitForBrowserNotInSet(std::set<Browser*> excluded_browsers) {
131  Browser* new_browser = GetBrowserNotInSet(excluded_browsers);
132  if (new_browser == NULL) {
133    BrowserAddedObserver observer;
134    new_browser = observer.WaitForSingleNewBrowser();
135    // The new browser should never be in |excluded_browsers|.
136    DCHECK(!ContainsKey(excluded_browsers, new_browser));
137  }
138  return new_browser;
139}
140
141}  // namespace
142
143bool GetCurrentTabTitle(const Browser* browser, base::string16* title) {
144  WebContents* web_contents =
145      browser->tab_strip_model()->GetActiveWebContents();
146  if (!web_contents)
147    return false;
148  NavigationEntry* last_entry = web_contents->GetController().GetActiveEntry();
149  if (!last_entry)
150    return false;
151  title->assign(last_entry->GetTitleForDisplay(std::string()));
152  return true;
153}
154
155Browser* OpenURLOffTheRecord(Profile* profile, const GURL& url) {
156  chrome::HostDesktopType active_desktop = chrome::GetActiveDesktop();
157  chrome::OpenURLOffTheRecord(profile, url, active_desktop);
158  Browser* browser = chrome::FindTabbedBrowser(
159      profile->GetOffTheRecordProfile(), false, active_desktop);
160  content::TestNavigationObserver observer(
161      browser->tab_strip_model()->GetActiveWebContents());
162  observer.Wait();
163  return browser;
164}
165
166void NavigateToURL(chrome::NavigateParams* params) {
167  chrome::Navigate(params);
168  content::WaitForLoadStop(params->target_contents);
169}
170
171
172void NavigateToURLWithPost(Browser* browser, const GURL& url) {
173  chrome::NavigateParams params(browser, url,
174                                content::PAGE_TRANSITION_FORM_SUBMIT);
175  params.uses_post = true;
176  NavigateToURL(&params);
177}
178
179void NavigateToURL(Browser* browser, const GURL& url) {
180  NavigateToURLWithDisposition(browser, url, CURRENT_TAB,
181                               BROWSER_TEST_WAIT_FOR_NAVIGATION);
182}
183
184// Navigates the specified tab (via |disposition|) of |browser| to |url|,
185// blocking until the |number_of_navigations| specified complete.
186// |disposition| indicates what tab the download occurs in, and
187// |browser_test_flags| controls what to wait for before continuing.
188static void NavigateToURLWithDispositionBlockUntilNavigationsComplete(
189    Browser* browser,
190    const GURL& url,
191    int number_of_navigations,
192    WindowOpenDisposition disposition,
193    int browser_test_flags) {
194  TabStripModel* tab_strip = browser->tab_strip_model();
195  if (disposition == CURRENT_TAB && tab_strip->GetActiveWebContents())
196      content::WaitForLoadStop(tab_strip->GetActiveWebContents());
197  content::TestNavigationObserver same_tab_observer(
198      tab_strip->GetActiveWebContents(),
199      number_of_navigations);
200
201  std::set<Browser*> initial_browsers;
202  for (chrome::BrowserIterator it; !it.done(); it.Next())
203    initial_browsers.insert(*it);
204
205  content::WindowedNotificationObserver tab_added_observer(
206      chrome::NOTIFICATION_TAB_ADDED,
207      content::NotificationService::AllSources());
208
209  browser->OpenURL(OpenURLParams(
210      url, Referrer(), disposition, content::PAGE_TRANSITION_TYPED, false));
211  if (browser_test_flags & BROWSER_TEST_WAIT_FOR_BROWSER)
212    browser = WaitForBrowserNotInSet(initial_browsers);
213  if (browser_test_flags & BROWSER_TEST_WAIT_FOR_TAB)
214    tab_added_observer.Wait();
215  if (!(browser_test_flags & BROWSER_TEST_WAIT_FOR_NAVIGATION)) {
216    // Some other flag caused the wait prior to this.
217    return;
218  }
219  WebContents* web_contents = NULL;
220  if (disposition == NEW_BACKGROUND_TAB) {
221    // We've opened up a new tab, but not selected it.
222    TabStripModel* tab_strip = browser->tab_strip_model();
223    web_contents = tab_strip->GetWebContentsAt(tab_strip->active_index() + 1);
224    EXPECT_TRUE(web_contents != NULL)
225        << " Unable to wait for navigation to \"" << url.spec()
226        << "\" because the new tab is not available yet";
227    if (!web_contents)
228      return;
229  } else if ((disposition == CURRENT_TAB) ||
230      (disposition == NEW_FOREGROUND_TAB) ||
231      (disposition == SINGLETON_TAB)) {
232    // The currently selected tab is the right one.
233    web_contents = browser->tab_strip_model()->GetActiveWebContents();
234  }
235  if (disposition == CURRENT_TAB) {
236    same_tab_observer.Wait();
237    return;
238  } else if (web_contents) {
239    content::TestNavigationObserver observer(web_contents,
240                                             number_of_navigations);
241    observer.Wait();
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
249void 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
261void 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
272void WaitUntilDevToolsWindowLoaded(DevToolsWindow* window) {
273  scoped_refptr<content::MessageLoopRunner> runner =
274      new content::MessageLoopRunner;
275  window->SetLoadCompletedCallback(runner->QuitClosure());
276  runner->Run();
277}
278
279base::FilePath GetTestFilePath(const base::FilePath& dir,
280                               const base::FilePath& file) {
281  base::FilePath path;
282  PathService::Get(chrome::DIR_TEST_DATA, &path);
283  return path.Append(dir).Append(file);
284}
285
286GURL GetTestUrl(const base::FilePath& dir, const base::FilePath& file) {
287  return net::FilePathToFileURL(GetTestFilePath(dir, file));
288}
289
290bool GetRelativeBuildDirectory(base::FilePath* build_dir) {
291  // This function is used to find the build directory so TestServer can serve
292  // built files (nexes, etc).  TestServer expects a path relative to the source
293  // root.
294  base::FilePath exe_dir =
295      CommandLine::ForCurrentProcess()->GetProgram().DirName();
296  base::FilePath src_dir;
297  if (!PathService::Get(base::DIR_SOURCE_ROOT, &src_dir))
298    return false;
299
300  // We must first generate absolute paths to SRC and EXE and from there
301  // generate a relative path.
302  if (!exe_dir.IsAbsolute())
303    exe_dir = base::MakeAbsoluteFilePath(exe_dir);
304  if (!src_dir.IsAbsolute())
305    src_dir = base::MakeAbsoluteFilePath(src_dir);
306  if (!exe_dir.IsAbsolute())
307    return false;
308  if (!src_dir.IsAbsolute())
309    return false;
310
311  size_t match, exe_size, src_size;
312  std::vector<base::FilePath::StringType> src_parts, exe_parts;
313
314  // Determine point at which src and exe diverge.
315  exe_dir.GetComponents(&exe_parts);
316  src_dir.GetComponents(&src_parts);
317  exe_size = exe_parts.size();
318  src_size = src_parts.size();
319  for (match = 0; match < exe_size && match < src_size; ++match) {
320    if (exe_parts[match] != src_parts[match])
321      break;
322  }
323
324  // Create a relative path.
325  *build_dir = base::FilePath();
326  for (size_t tmp_itr = match; tmp_itr < src_size; ++tmp_itr)
327    *build_dir = build_dir->Append(FILE_PATH_LITERAL(".."));
328  for (; match < exe_size; ++match)
329    *build_dir = build_dir->Append(exe_parts[match]);
330  return true;
331}
332
333AppModalDialog* WaitForAppModalDialog() {
334  AppModalDialogQueue* dialog_queue = AppModalDialogQueue::GetInstance();
335  if (dialog_queue->HasActiveDialog())
336    return dialog_queue->active_dialog();
337
338  content::WindowedNotificationObserver observer(
339      chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN,
340      content::NotificationService::AllSources());
341  observer.Wait();
342  return content::Source<AppModalDialog>(observer.source()).ptr();
343}
344
345int FindInPage(WebContents* tab,
346               const base::string16& search_string,
347               bool forward,
348               bool match_case,
349               int* ordinal,
350               gfx::Rect* selection_rect) {
351  FindTabHelper* find_tab_helper = FindTabHelper::FromWebContents(tab);
352  find_tab_helper->StartFinding(search_string, forward, match_case);
353  FindInPageNotificationObserver observer(tab);
354  observer.Wait();
355  if (ordinal)
356    *ordinal = observer.active_match_ordinal();
357  if (selection_rect)
358    *selection_rect = observer.selection_rect();
359  return observer.number_of_matches();
360}
361
362void WaitForTemplateURLServiceToLoad(TemplateURLService* service) {
363  if (service->loaded())
364    return;
365  scoped_refptr<content::MessageLoopRunner> message_loop_runner =
366      new content::MessageLoopRunner;
367  scoped_ptr<TemplateURLService::Subscription> subscription =
368      service->RegisterOnLoadedCallback(
369          message_loop_runner->QuitClosure());
370  service->Load();
371  message_loop_runner->Run();
372
373  ASSERT_TRUE(service->loaded());
374}
375
376void WaitForHistoryToLoad(HistoryService* history_service) {
377  content::WindowedNotificationObserver history_loaded_observer(
378      chrome::NOTIFICATION_HISTORY_LOADED,
379      content::NotificationService::AllSources());
380  if (!history_service->BackendLoaded())
381    history_loaded_observer.Wait();
382}
383
384void DownloadURL(Browser* browser, const GURL& download_url) {
385  base::ScopedTempDir downloads_directory;
386  ASSERT_TRUE(downloads_directory.CreateUniqueTempDir());
387  browser->profile()->GetPrefs()->SetFilePath(
388      prefs::kDownloadDefaultDirectory, downloads_directory.path());
389
390  content::DownloadManager* download_manager =
391      content::BrowserContext::GetDownloadManager(browser->profile());
392  scoped_ptr<content::DownloadTestObserver> observer(
393      new content::DownloadTestObserverTerminal(
394          download_manager, 1,
395          content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT));
396
397  ui_test_utils::NavigateToURL(browser, download_url);
398  observer->WaitForFinished();
399}
400
401void SendToOmniboxAndSubmit(LocationBar* location_bar,
402                            const std::string& input) {
403  OmniboxView* omnibox = location_bar->GetOmniboxView();
404  omnibox->model()->OnSetFocus(false);
405  omnibox->SetUserText(base::ASCIIToUTF16(input));
406  location_bar->AcceptInput();
407  while (!omnibox->model()->autocomplete_controller()->done()) {
408    content::WindowedNotificationObserver observer(
409        chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
410        content::NotificationService::AllSources());
411    observer.Wait();
412  }
413}
414
415Browser* GetBrowserNotInSet(std::set<Browser*> excluded_browsers) {
416  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
417    if (excluded_browsers.find(*it) == excluded_browsers.end())
418      return *it;
419  }
420  return NULL;
421}
422
423namespace {
424
425void GetCookiesCallback(base::WaitableEvent* event,
426                        std::string* cookies,
427                        const std::string& cookie_line) {
428  *cookies = cookie_line;
429  event->Signal();
430}
431
432void GetCookiesOnIOThread(
433    const GURL& url,
434    const scoped_refptr<net::URLRequestContextGetter>& context_getter,
435    base::WaitableEvent* event,
436    std::string* cookies) {
437  context_getter->GetURLRequestContext()->cookie_store()->
438      GetCookiesWithOptionsAsync(
439          url, net::CookieOptions(),
440          base::Bind(&GetCookiesCallback, event, cookies));
441}
442
443}  // namespace
444
445void GetCookies(const GURL& url,
446                WebContents* contents,
447                int* value_size,
448                std::string* value) {
449  *value_size = -1;
450  if (url.is_valid() && contents) {
451    scoped_refptr<net::URLRequestContextGetter> context_getter =
452        contents->GetBrowserContext()->GetRequestContextForRenderProcess(
453            contents->GetRenderProcessHost()->GetID());
454    base::WaitableEvent event(true /* manual reset */,
455                              false /* not initially signaled */);
456    CHECK(content::BrowserThread::PostTask(
457        content::BrowserThread::IO, FROM_HERE,
458        base::Bind(&GetCookiesOnIOThread, url, context_getter, &event, value)));
459    event.Wait();
460
461    *value_size = static_cast<int>(value->size());
462  }
463}
464
465WindowedTabAddedNotificationObserver::WindowedTabAddedNotificationObserver(
466    const content::NotificationSource& source)
467    : WindowedNotificationObserver(chrome::NOTIFICATION_TAB_ADDED, source),
468      added_tab_(NULL) {
469}
470
471void WindowedTabAddedNotificationObserver::Observe(
472    int type,
473    const content::NotificationSource& source,
474    const content::NotificationDetails& details) {
475  added_tab_ = content::Details<WebContents>(details).ptr();
476  content::WindowedNotificationObserver::Observe(type, source, details);
477}
478
479UrlLoadObserver::UrlLoadObserver(const GURL& url,
480                                 const content::NotificationSource& source)
481    : WindowedNotificationObserver(content::NOTIFICATION_LOAD_STOP, source),
482      url_(url) {
483}
484
485UrlLoadObserver::~UrlLoadObserver() {}
486
487void UrlLoadObserver::Observe(
488    int type,
489    const content::NotificationSource& source,
490    const content::NotificationDetails& details) {
491  NavigationController* controller =
492      content::Source<NavigationController>(source).ptr();
493  if (controller->GetWebContents()->GetURL() != url_)
494    return;
495
496  WindowedNotificationObserver::Observe(type, source, details);
497}
498
499BrowserAddedObserver::BrowserAddedObserver()
500    : notification_observer_(
501          chrome::NOTIFICATION_BROWSER_OPENED,
502          content::NotificationService::AllSources()) {
503  for (chrome::BrowserIterator it; !it.done(); it.Next())
504    original_browsers_.insert(*it);
505}
506
507BrowserAddedObserver::~BrowserAddedObserver() {
508}
509
510Browser* BrowserAddedObserver::WaitForSingleNewBrowser() {
511  notification_observer_.Wait();
512  // Ensure that only a single new browser has appeared.
513  EXPECT_EQ(original_browsers_.size() + 1, chrome::GetTotalBrowserCount());
514  return GetBrowserNotInSet(original_browsers_);
515}
516
517#if defined(OS_WIN)
518
519bool SaveScreenSnapshotToDirectory(const base::FilePath& directory,
520                                   base::FilePath* screenshot_path) {
521  bool succeeded = false;
522  base::FilePath out_path(GetSnapshotFileName(directory));
523
524  MONITORINFO monitor_info = {};
525  monitor_info.cbSize = sizeof(monitor_info);
526  HMONITOR main_monitor = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY);
527  if (GetMonitorInfo(main_monitor, &monitor_info)) {
528    RECT& rect = monitor_info.rcMonitor;
529
530    std::vector<unsigned char> png_data;
531    gfx::Rect bounds(
532        gfx::Size(rect.right - rect.left, rect.bottom - rect.top));
533    if (ui::GrabDesktopSnapshot(bounds, &png_data) &&
534        png_data.size() <= INT_MAX) {
535      int bytes = static_cast<int>(png_data.size());
536      int written = base::WriteFile(
537          out_path, reinterpret_cast<char*>(&png_data[0]), bytes);
538      succeeded = (written == bytes);
539    }
540  }
541
542  if (succeeded && screenshot_path != NULL)
543    *screenshot_path = out_path;
544
545  return succeeded;
546}
547
548bool SaveScreenSnapshotToDesktop(base::FilePath* screenshot_path) {
549  base::FilePath desktop;
550
551  return PathService::Get(base::DIR_USER_DESKTOP, &desktop) &&
552      SaveScreenSnapshotToDirectory(desktop, screenshot_path);
553}
554
555#endif  // defined(OS_WIN)
556
557void OverrideGeolocation(double latitude, double longitude) {
558  content::Geoposition position;
559  position.latitude = latitude;
560  position.longitude = longitude;
561  position.altitude = 0.;
562  position.accuracy = 0.;
563  position.timestamp = base::Time::Now();
564  content::GeolocationProvider::GetInstance()->OverrideLocationForTesting(
565      position);
566}
567
568HistoryEnumerator::HistoryEnumerator(Profile* profile) {
569  scoped_refptr<content::MessageLoopRunner> message_loop_runner =
570      new content::MessageLoopRunner;
571
572  HistoryService* hs = HistoryServiceFactory::GetForProfile(
573      profile, Profile::EXPLICIT_ACCESS);
574  hs->QueryHistory(
575      base::string16(),
576      history::QueryOptions(),
577      &consumer_,
578      base::Bind(&HistoryEnumerator::HistoryQueryComplete,
579                 base::Unretained(this), message_loop_runner->QuitClosure()));
580  message_loop_runner->Run();
581}
582
583HistoryEnumerator::~HistoryEnumerator() {}
584
585void HistoryEnumerator::HistoryQueryComplete(
586    const base::Closure& quit_task,
587    HistoryService::Handle request_handle,
588    history::QueryResults* results) {
589  for (size_t i = 0; i < results->size(); ++i)
590    urls_.push_back((*results)[i].url());
591  quit_task.Run();
592}
593
594}  // namespace ui_test_utils
595