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.
5 #include "chrome/test/base/ui_test_utils.h"
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/files/file_path.h"
16 #include "base/files/file_util.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "base/path_service.h"
20 #include "base/prefs/pref_service.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/test/test_timeouts.h"
24 #include "base/time/time.h"
25 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
26 #include "chrome/browser/chrome_notification_types.h"
27 #include "chrome/browser/history/history_service_factory.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/browser/ui/browser.h"
30 #include "chrome/browser/ui/browser_commands.h"
31 #include "chrome/browser/ui/browser_finder.h"
32 #include "chrome/browser/ui/browser_iterator.h"
33 #include "chrome/browser/ui/browser_list.h"
34 #include "chrome/browser/ui/browser_navigator.h"
35 #include "chrome/browser/ui/browser_window.h"
36 #include "chrome/browser/ui/find_bar/find_notification_details.h"
37 #include "chrome/browser/ui/find_bar/find_tab_helper.h"
38 #include "chrome/browser/ui/host_desktop.h"
39 #include "chrome/browser/ui/location_bar/location_bar.h"
40 #include "chrome/browser/ui/omnibox/omnibox_view.h"
41 #include "chrome/browser/ui/tabs/tab_strip_model.h"
42 #include "chrome/common/chrome_paths.h"
43 #include "chrome/common/pref_names.h"
44 #include "chrome/test/base/find_in_page_observer.h"
45 #include "components/app_modal/app_modal_dialog.h"
46 #include "components/app_modal/app_modal_dialog_queue.h"
47 #include "components/bookmarks/browser/bookmark_model.h"
48 #include "components/history/core/browser/history_service_observer.h"
49 #include "components/omnibox/browser/autocomplete_controller.h"
50 #include "components/search_engines/template_url_service.h"
51 #include "content/public/browser/dom_operation_notification_details.h"
52 #include "content/public/browser/download_item.h"
53 #include "content/public/browser/download_manager.h"
54 #include "content/public/browser/geolocation_provider.h"
55 #include "content/public/browser/navigation_controller.h"
56 #include "content/public/browser/navigation_entry.h"
57 #include "content/public/browser/notification_service.h"
58 #include "content/public/browser/render_process_host.h"
59 #include "content/public/browser/web_contents.h"
60 #include "content/public/browser/web_contents_observer.h"
61 #include "content/public/common/geoposition.h"
62 #include "content/public/test/browser_test_utils.h"
63 #include "content/public/test/download_test_observer.h"
64 #include "content/public/test/test_navigation_observer.h"
65 #include "content/public/test/test_utils.h"
66 #include "net/base/filename_util.h"
67 #include "net/cookies/cookie_constants.h"
68 #include "net/cookies/cookie_monster.h"
69 #include "net/cookies/cookie_store.h"
70 #include "net/test/python_utils.h"
71 #include "net/url_request/url_request_context.h"
72 #include "net/url_request/url_request_context_getter.h"
73 #include "ui/gfx/geometry/rect.h"
76 #include "ash/shell.h"
77 #include "ui/aura/window_event_dispatcher.h"
80 using content::DomOperationNotificationDetails
;
81 using content::NativeWebKeyboardEvent
;
82 using content::NavigationController
;
83 using content::NavigationEntry
;
84 using content::OpenURLParams
;
85 using content::Referrer
;
86 using content::WebContents
;
88 namespace ui_test_utils
{
92 Browser
* WaitForBrowserNotInSet(std::set
<Browser
*> excluded_browsers
) {
93 Browser
* new_browser
= GetBrowserNotInSet(excluded_browsers
);
94 if (new_browser
== NULL
) {
95 BrowserAddedObserver observer
;
96 new_browser
= observer
.WaitForSingleNewBrowser();
97 // The new browser should never be in |excluded_browsers|.
98 DCHECK(!ContainsKey(excluded_browsers
, new_browser
));
103 class AppModalDialogWaiter
: public app_modal::AppModalDialogObserver
{
105 AppModalDialogWaiter()
108 ~AppModalDialogWaiter() override
{
111 app_modal::AppModalDialog
* Wait() {
114 message_loop_runner_
= new content::MessageLoopRunner
;
115 message_loop_runner_
->Run();
116 EXPECT_TRUE(dialog_
);
120 // AppModalDialogWaiter:
121 void Notify(app_modal::AppModalDialog
* dialog
) override
{
124 if (message_loop_runner_
.get() && message_loop_runner_
->loop_running())
125 message_loop_runner_
->Quit();
129 app_modal::AppModalDialog
* dialog_
;
130 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
132 DISALLOW_COPY_AND_ASSIGN(AppModalDialogWaiter
);
137 bool GetCurrentTabTitle(const Browser
* browser
, base::string16
* title
) {
138 WebContents
* web_contents
=
139 browser
->tab_strip_model()->GetActiveWebContents();
142 NavigationEntry
* last_entry
= web_contents
->GetController().GetActiveEntry();
145 title
->assign(last_entry
->GetTitleForDisplay(std::string()));
149 void NavigateToURL(chrome::NavigateParams
* params
) {
150 chrome::Navigate(params
);
151 content::WaitForLoadStop(params
->target_contents
);
155 void NavigateToURLWithPost(Browser
* browser
, const GURL
& url
) {
156 chrome::NavigateParams
params(browser
, url
,
157 ui::PAGE_TRANSITION_FORM_SUBMIT
);
158 params
.uses_post
= true;
159 NavigateToURL(¶ms
);
162 void NavigateToURL(Browser
* browser
, const GURL
& url
) {
163 NavigateToURLWithDisposition(browser
, url
, CURRENT_TAB
,
164 BROWSER_TEST_WAIT_FOR_NAVIGATION
);
167 void NavigateToURLWithDispositionBlockUntilNavigationsComplete(
170 int number_of_navigations
,
171 WindowOpenDisposition disposition
,
172 int browser_test_flags
) {
173 TabStripModel
* tab_strip
= browser
->tab_strip_model();
174 if (disposition
== CURRENT_TAB
&& tab_strip
->GetActiveWebContents())
175 content::WaitForLoadStop(tab_strip
->GetActiveWebContents());
176 content::TestNavigationObserver
same_tab_observer(
177 tab_strip
->GetActiveWebContents(),
178 number_of_navigations
);
180 std::set
<Browser
*> initial_browsers
;
181 for (chrome::BrowserIterator it
; !it
.done(); it
.Next())
182 initial_browsers
.insert(*it
);
184 content::WindowedNotificationObserver
tab_added_observer(
185 chrome::NOTIFICATION_TAB_ADDED
,
186 content::NotificationService::AllSources());
188 browser
->OpenURL(OpenURLParams(
189 url
, Referrer(), disposition
, ui::PAGE_TRANSITION_TYPED
, false));
190 if (browser_test_flags
& BROWSER_TEST_WAIT_FOR_BROWSER
)
191 browser
= WaitForBrowserNotInSet(initial_browsers
);
192 if (browser_test_flags
& BROWSER_TEST_WAIT_FOR_TAB
)
193 tab_added_observer
.Wait();
194 if (!(browser_test_flags
& BROWSER_TEST_WAIT_FOR_NAVIGATION
)) {
195 // Some other flag caused the wait prior to this.
198 WebContents
* web_contents
= NULL
;
199 if (disposition
== NEW_BACKGROUND_TAB
) {
200 // We've opened up a new tab, but not selected it.
201 TabStripModel
* tab_strip
= browser
->tab_strip_model();
202 web_contents
= tab_strip
->GetWebContentsAt(tab_strip
->active_index() + 1);
203 EXPECT_TRUE(web_contents
!= NULL
)
204 << " Unable to wait for navigation to \"" << url
.spec()
205 << "\" because the new tab is not available yet";
208 } else if ((disposition
== CURRENT_TAB
) ||
209 (disposition
== NEW_FOREGROUND_TAB
) ||
210 (disposition
== SINGLETON_TAB
)) {
211 // The currently selected tab is the right one.
212 web_contents
= browser
->tab_strip_model()->GetActiveWebContents();
214 if (disposition
== CURRENT_TAB
) {
215 same_tab_observer
.Wait();
217 } else if (web_contents
) {
218 content::TestNavigationObserver
observer(web_contents
,
219 number_of_navigations
);
223 EXPECT_TRUE(NULL
!= web_contents
) << " Unable to wait for navigation to \""
224 << url
.spec() << "\""
225 << " because we can't get the tab contents";
228 void NavigateToURLWithDisposition(Browser
* browser
,
230 WindowOpenDisposition disposition
,
231 int browser_test_flags
) {
232 NavigateToURLWithDispositionBlockUntilNavigationsComplete(
240 void NavigateToURLBlockUntilNavigationsComplete(Browser
* browser
,
242 int number_of_navigations
) {
243 NavigateToURLWithDispositionBlockUntilNavigationsComplete(
246 number_of_navigations
,
248 BROWSER_TEST_WAIT_FOR_NAVIGATION
);
251 base::FilePath
GetTestFilePath(const base::FilePath
& dir
,
252 const base::FilePath
& file
) {
254 PathService::Get(chrome::DIR_TEST_DATA
, &path
);
255 return path
.Append(dir
).Append(file
);
258 GURL
GetTestUrl(const base::FilePath
& dir
, const base::FilePath
& file
) {
259 return net::FilePathToFileURL(GetTestFilePath(dir
, file
));
262 bool GetRelativeBuildDirectory(base::FilePath
* build_dir
) {
263 // This function is used to find the build directory so TestServer can serve
264 // built files (nexes, etc). TestServer expects a path relative to the source
266 base::FilePath exe_dir
=
267 base::CommandLine::ForCurrentProcess()->GetProgram().DirName();
268 base::FilePath src_dir
;
269 if (!PathService::Get(base::DIR_SOURCE_ROOT
, &src_dir
))
272 // We must first generate absolute paths to SRC and EXE and from there
273 // generate a relative path.
274 if (!exe_dir
.IsAbsolute())
275 exe_dir
= base::MakeAbsoluteFilePath(exe_dir
);
276 if (!src_dir
.IsAbsolute())
277 src_dir
= base::MakeAbsoluteFilePath(src_dir
);
278 if (!exe_dir
.IsAbsolute())
280 if (!src_dir
.IsAbsolute())
283 size_t match
, exe_size
, src_size
;
284 std::vector
<base::FilePath::StringType
> src_parts
, exe_parts
;
286 // Determine point at which src and exe diverge.
287 exe_dir
.GetComponents(&exe_parts
);
288 src_dir
.GetComponents(&src_parts
);
289 exe_size
= exe_parts
.size();
290 src_size
= src_parts
.size();
291 for (match
= 0; match
< exe_size
&& match
< src_size
; ++match
) {
292 if (exe_parts
[match
] != src_parts
[match
])
296 // Create a relative path.
297 *build_dir
= base::FilePath();
298 for (size_t tmp_itr
= match
; tmp_itr
< src_size
; ++tmp_itr
)
299 *build_dir
= build_dir
->Append(FILE_PATH_LITERAL(".."));
300 for (; match
< exe_size
; ++match
)
301 *build_dir
= build_dir
->Append(exe_parts
[match
]);
305 app_modal::AppModalDialog
* WaitForAppModalDialog() {
306 app_modal::AppModalDialogQueue
* dialog_queue
=
307 app_modal::AppModalDialogQueue::GetInstance();
308 if (dialog_queue
->HasActiveDialog())
309 return dialog_queue
->active_dialog();
310 AppModalDialogWaiter waiter
;
311 return waiter
.Wait();
314 int FindInPage(WebContents
* tab
,
315 const base::string16
& search_string
,
319 gfx::Rect
* selection_rect
) {
320 FindTabHelper
* find_tab_helper
= FindTabHelper::FromWebContents(tab
);
321 find_tab_helper
->StartFinding(search_string
, forward
, match_case
);
322 FindInPageNotificationObserver
observer(tab
);
325 *ordinal
= observer
.active_match_ordinal();
327 *selection_rect
= observer
.selection_rect();
328 return observer
.number_of_matches();
331 void WaitForTemplateURLServiceToLoad(TemplateURLService
* service
) {
332 if (service
->loaded())
334 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner
=
335 new content::MessageLoopRunner
;
336 scoped_ptr
<TemplateURLService::Subscription
> subscription
=
337 service
->RegisterOnLoadedCallback(
338 message_loop_runner
->QuitClosure());
340 message_loop_runner
->Run();
342 ASSERT_TRUE(service
->loaded());
345 void DownloadURL(Browser
* browser
, const GURL
& download_url
) {
346 base::ScopedTempDir downloads_directory
;
347 ASSERT_TRUE(downloads_directory
.CreateUniqueTempDir());
348 browser
->profile()->GetPrefs()->SetFilePath(
349 prefs::kDownloadDefaultDirectory
, downloads_directory
.path());
351 content::DownloadManager
* download_manager
=
352 content::BrowserContext::GetDownloadManager(browser
->profile());
353 scoped_ptr
<content::DownloadTestObserver
> observer(
354 new content::DownloadTestObserverTerminal(
356 content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT
));
358 ui_test_utils::NavigateToURL(browser
, download_url
);
359 observer
->WaitForFinished();
362 void SendToOmniboxAndSubmit(LocationBar
* location_bar
,
363 const std::string
& input
) {
364 OmniboxView
* omnibox
= location_bar
->GetOmniboxView();
365 omnibox
->model()->OnSetFocus(false);
366 omnibox
->SetUserText(base::ASCIIToUTF16(input
));
367 location_bar
->AcceptInput();
368 while (!omnibox
->model()->autocomplete_controller()->done()) {
369 content::WindowedNotificationObserver
observer(
370 chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY
,
371 content::NotificationService::AllSources());
376 Browser
* GetBrowserNotInSet(std::set
<Browser
*> excluded_browsers
) {
377 for (chrome::BrowserIterator it
; !it
.done(); it
.Next()) {
378 if (excluded_browsers
.find(*it
) == excluded_browsers
.end())
386 void GetCookiesCallback(base::WaitableEvent
* event
,
387 std::string
* cookies
,
388 const std::string
& cookie_line
) {
389 *cookies
= cookie_line
;
393 void GetCookiesOnIOThread(
395 const scoped_refptr
<net::URLRequestContextGetter
>& context_getter
,
396 base::WaitableEvent
* event
,
397 std::string
* cookies
) {
398 context_getter
->GetURLRequestContext()->cookie_store()->
399 GetCookiesWithOptionsAsync(
400 url
, net::CookieOptions(),
401 base::Bind(&GetCookiesCallback
, event
, cookies
));
406 void GetCookies(const GURL
& url
,
407 WebContents
* contents
,
409 std::string
* value
) {
411 if (url
.is_valid() && contents
) {
412 scoped_refptr
<net::URLRequestContextGetter
> context_getter
=
413 contents
->GetBrowserContext()->GetRequestContextForRenderProcess(
414 contents
->GetRenderProcessHost()->GetID());
415 base::WaitableEvent
event(true /* manual reset */,
416 false /* not initially signaled */);
417 CHECK(content::BrowserThread::PostTask(
418 content::BrowserThread::IO
, FROM_HERE
,
419 base::Bind(&GetCookiesOnIOThread
, url
, context_getter
, &event
, value
)));
422 *value_size
= static_cast<int>(value
->size());
426 WindowedTabAddedNotificationObserver::WindowedTabAddedNotificationObserver(
427 const content::NotificationSource
& source
)
428 : WindowedNotificationObserver(chrome::NOTIFICATION_TAB_ADDED
, source
),
432 void WindowedTabAddedNotificationObserver::Observe(
434 const content::NotificationSource
& source
,
435 const content::NotificationDetails
& details
) {
436 added_tab_
= content::Details
<WebContents
>(details
).ptr();
437 content::WindowedNotificationObserver::Observe(type
, source
, details
);
440 UrlLoadObserver::UrlLoadObserver(const GURL
& url
,
441 const content::NotificationSource
& source
)
442 : WindowedNotificationObserver(content::NOTIFICATION_LOAD_STOP
, source
),
446 UrlLoadObserver::~UrlLoadObserver() {}
448 void UrlLoadObserver::Observe(
450 const content::NotificationSource
& source
,
451 const content::NotificationDetails
& details
) {
452 NavigationController
* controller
=
453 content::Source
<NavigationController
>(source
).ptr();
454 if (controller
->GetWebContents()->GetURL() != url_
)
457 WindowedNotificationObserver::Observe(type
, source
, details
);
460 BrowserAddedObserver::BrowserAddedObserver()
461 : notification_observer_(
462 chrome::NOTIFICATION_BROWSER_OPENED
,
463 content::NotificationService::AllSources()) {
464 for (chrome::BrowserIterator it
; !it
.done(); it
.Next())
465 original_browsers_
.insert(*it
);
468 BrowserAddedObserver::~BrowserAddedObserver() {
471 Browser
* BrowserAddedObserver::WaitForSingleNewBrowser() {
472 notification_observer_
.Wait();
473 // Ensure that only a single new browser has appeared.
474 EXPECT_EQ(original_browsers_
.size() + 1, chrome::GetTotalBrowserCount());
475 return GetBrowserNotInSet(original_browsers_
);
478 void OverrideGeolocation(double latitude
, double longitude
) {
479 content::Geoposition position
;
480 position
.latitude
= latitude
;
481 position
.longitude
= longitude
;
482 position
.altitude
= 0.;
483 position
.accuracy
= 0.;
484 position
.timestamp
= base::Time::Now();
485 content::GeolocationProvider::GetInstance()->OverrideLocationForTesting(
489 HistoryEnumerator::HistoryEnumerator(Profile
* profile
) {
490 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner
=
491 new content::MessageLoopRunner
;
493 history::HistoryService
* hs
= HistoryServiceFactory::GetForProfile(
494 profile
, ServiceAccessType::EXPLICIT_ACCESS
);
495 hs
->QueryHistory(base::string16(),
496 history::QueryOptions(),
497 base::Bind(&HistoryEnumerator::HistoryQueryComplete
,
498 base::Unretained(this),
499 message_loop_runner
->QuitClosure()),
501 message_loop_runner
->Run();
504 HistoryEnumerator::~HistoryEnumerator() {}
506 void HistoryEnumerator::HistoryQueryComplete(
507 const base::Closure
& quit_task
,
508 history::QueryResults
* results
) {
509 for (size_t i
= 0; i
< results
->size(); ++i
)
510 urls_
.push_back((*results
)[i
].url());
514 // Wait for HistoryService to load.
515 class WaitHistoryLoadedObserver
: public history::HistoryServiceObserver
{
517 explicit WaitHistoryLoadedObserver(content::MessageLoopRunner
* runner
);
518 ~WaitHistoryLoadedObserver() override
;
520 // history::HistoryServiceObserver:
521 void OnHistoryServiceLoaded(history::HistoryService
* service
) override
;
525 content::MessageLoopRunner
* runner_
;
528 WaitHistoryLoadedObserver::WaitHistoryLoadedObserver(
529 content::MessageLoopRunner
* runner
)
533 WaitHistoryLoadedObserver::~WaitHistoryLoadedObserver() {
536 void WaitHistoryLoadedObserver::OnHistoryServiceLoaded(
537 history::HistoryService
* service
) {
541 void WaitForHistoryToLoad(history::HistoryService
* history_service
) {
542 if (!history_service
->BackendLoaded()) {
543 scoped_refptr
<content::MessageLoopRunner
> runner
=
544 new content::MessageLoopRunner
;
545 WaitHistoryLoadedObserver
observer(runner
.get());
546 ScopedObserver
<history::HistoryService
, history::HistoryServiceObserver
>
547 scoped_observer(&observer
);
548 scoped_observer
.Add(history_service
);
553 } // namespace ui_test_utils