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