1 // Copyright 2014 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.
7 #include "base/message_loop/message_loop.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/synchronization/waitable_event.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager.h"
14 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager_factory.h"
15 #include "chrome/browser/chromeos/login/signin_specifics.h"
16 #include "chrome/browser/chromeos/login/startup_utils.h"
17 #include "chrome/browser/chromeos/login/test/oobe_base_test.h"
18 #include "chrome/browser/chromeos/login/wizard_controller.h"
19 #include "chrome/browser/profiles/profile_manager.h"
20 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_tabstrip.h"
23 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model.h"
25 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "chrome/test/base/ui_test_utils.h"
28 #include "chromeos/login/auth/key.h"
29 #include "chromeos/login/auth/user_context.h"
30 #include "components/app_modal/javascript_app_modal_dialog.h"
31 #include "components/app_modal/native_app_modal_dialog.h"
32 #include "components/signin/core/browser/account_tracker_service.h"
33 #include "components/signin/core/browser/profile_oauth2_token_service.h"
34 #include "components/user_manager/user.h"
35 #include "components/user_manager/user_manager.h"
36 #include "content/public/browser/notification_service.h"
37 #include "content/public/test/browser_test_utils.h"
38 #include "extensions/browser/process_manager.h"
39 #include "extensions/test/extension_test_message_listener.h"
40 #include "extensions/test/result_catcher.h"
41 #include "google_apis/gaia/gaia_constants.h"
42 #include "google_apis/gaia/gaia_urls.h"
43 #include "net/cookies/canonical_cookie.h"
44 #include "net/cookies/cookie_monster.h"
45 #include "net/cookies/cookie_store.h"
46 #include "net/test/embedded_test_server/http_request.h"
47 #include "net/test/embedded_test_server/http_response.h"
48 #include "net/url_request/url_request_context.h"
49 #include "net/url_request/url_request_context_getter.h"
51 using app_modal::AppModalDialog
;
52 using app_modal::JavaScriptAppModalDialog
;
53 using net::test_server::BasicHttpResponse
;
54 using net::test_server::HttpRequest
;
55 using net::test_server::HttpResponse
;
61 // Email of owner account for test.
62 const char kTestGaiaId
[] = "12345";
63 const char kTestEmail
[] = "username@gmail.com";
64 const char kTestRawEmail
[] = "User.Name@gmail.com";
65 const char kTestAccountPassword
[] = "fake-password";
66 const char kTestAuthCode
[] = "fake-auth-code";
67 const char kTestGaiaUberToken
[] = "fake-uber-token";
68 const char kTestAuthLoginAccessToken
[] = "fake-access-token";
69 const char kTestRefreshToken
[] = "fake-refresh-token";
70 const char kTestAuthSIDCookie
[] = "fake-auth-SID-cookie";
71 const char kTestAuthLSIDCookie
[] = "fake-auth-LSID-cookie";
72 const char kTestSessionSIDCookie
[] = "fake-session-SID-cookie";
73 const char kTestSessionLSIDCookie
[] = "fake-session-LSID-cookie";
74 const char kTestSession2SIDCookie
[] = "fake-session2-SID-cookie";
75 const char kTestSession2LSIDCookie
[] = "fake-session2-LSID-cookie";
76 const char kTestUserinfoToken
[] = "fake-userinfo-token";
77 const char kTestLoginToken
[] = "fake-login-token";
78 const char kTestSyncToken
[] = "fake-sync-token";
79 const char kTestAuthLoginToken
[] = "fake-oauthlogin-token";
81 std::string
PickAccountId(Profile
* profile
,
82 const std::string
& gaia_id
,
83 const std::string
& email
) {
84 return AccountTrackerService::PickAccountIdForAccount(profile
->GetPrefs(),
88 class OAuth2LoginManagerStateWaiter
: public OAuth2LoginManager::Observer
{
90 explicit OAuth2LoginManagerStateWaiter(Profile
* profile
)
92 waiting_for_state_(false),
93 final_state_(OAuth2LoginManager::SESSION_RESTORE_NOT_STARTED
) {
97 const std::set
<OAuth2LoginManager::SessionRestoreState
>& states
) {
98 DCHECK(!waiting_for_state_
);
99 OAuth2LoginManager
* login_manager
=
100 OAuth2LoginManagerFactory::GetInstance()->GetForProfile(profile_
);
102 if (states_
.find(login_manager
->state()) != states_
.end()) {
103 final_state_
= login_manager
->state();
107 waiting_for_state_
= true;
108 login_manager
->AddObserver(this);
109 runner_
= new content::MessageLoopRunner
;
111 login_manager
->RemoveObserver(this);
114 OAuth2LoginManager::SessionRestoreState
final_state() { return final_state_
; }
117 // OAuth2LoginManager::Observer overrides.
118 void OnSessionRestoreStateChanged(
119 Profile
* user_profile
,
120 OAuth2LoginManager::SessionRestoreState state
) override
{
121 if (!waiting_for_state_
)
124 if (states_
.find(state
) == states_
.end())
127 final_state_
= state
;
128 waiting_for_state_
= false;
133 std::set
<OAuth2LoginManager::SessionRestoreState
> states_
;
134 bool waiting_for_state_
;
135 OAuth2LoginManager::SessionRestoreState final_state_
;
136 scoped_refptr
<content::MessageLoopRunner
> runner_
;
138 DISALLOW_COPY_AND_ASSIGN(OAuth2LoginManagerStateWaiter
);
143 class OAuth2Test
: public OobeBaseTest
{
147 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
148 OobeBaseTest::SetUpCommandLine(command_line
);
150 // Disable sync sinc we don't really need this for these tests and it also
151 // makes OAuth2Test.MergeSession test flaky http://crbug.com/408867.
152 command_line
->AppendSwitch(switches::kDisableSync
);
155 void SetupGaiaServerForNewAccount() {
156 FakeGaia::MergeSessionParams params
;
157 params
.auth_sid_cookie
= kTestAuthSIDCookie
;
158 params
.auth_lsid_cookie
= kTestAuthLSIDCookie
;
159 params
.auth_code
= kTestAuthCode
;
160 params
.refresh_token
= kTestRefreshToken
;
161 params
.access_token
= kTestAuthLoginAccessToken
;
162 params
.gaia_uber_token
= kTestGaiaUberToken
;
163 params
.session_sid_cookie
= kTestSessionSIDCookie
;
164 params
.session_lsid_cookie
= kTestSessionLSIDCookie
;
165 fake_gaia_
->SetMergeSessionParams(params
);
166 SetupGaiaServerWithAccessTokens();
169 void SetupGaiaServerForUnexpiredAccount() {
170 FakeGaia::MergeSessionParams params
;
171 params
.email
= kTestEmail
;
172 fake_gaia_
->SetMergeSessionParams(params
);
173 SetupGaiaServerWithAccessTokens();
176 void SetupGaiaServerForExpiredAccount() {
177 FakeGaia::MergeSessionParams params
;
178 params
.gaia_uber_token
= kTestGaiaUberToken
;
179 params
.session_sid_cookie
= kTestSession2SIDCookie
;
180 params
.session_lsid_cookie
= kTestSession2LSIDCookie
;
181 fake_gaia_
->SetMergeSessionParams(params
);
182 SetupGaiaServerWithAccessTokens();
185 void LoginAsExistingUser() {
186 content::WindowedNotificationObserver(
187 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
,
188 content::NotificationService::AllSources()).Wait();
190 JsExpect("!!document.querySelector('#account-picker')");
191 JsExpect("!!document.querySelector('#pod-row')");
193 std::string account_id
= PickAccountId(
194 ProfileManager::GetPrimaryUserProfile(), kTestGaiaId
, kTestEmail
);
196 EXPECT_EQ(GetOAuthStatusFromLocalState(account_id
),
197 user_manager::User::OAUTH2_TOKEN_STATUS_VALID
);
199 // Try login. Primary profile has changed.
200 EXPECT_TRUE(TryToLogin(kTestGaiaId
, kTestEmail
, kTestAccountPassword
));
201 Profile
* profile
= ProfileManager::GetPrimaryUserProfile();
203 // Wait for the session merge to finish.
204 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_DONE
);
206 // Check for existance of refresh token.
207 ProfileOAuth2TokenService
* token_service
=
208 ProfileOAuth2TokenServiceFactory::GetForProfile(profile
);
209 EXPECT_TRUE(token_service
->RefreshTokenIsAvailable(account_id
));
211 EXPECT_EQ(GetOAuthStatusFromLocalState(account_id
),
212 user_manager::User::OAUTH2_TOKEN_STATUS_VALID
);
215 bool TryToLogin(const std::string
& gaia_id
,
216 const std::string
& username
,
217 const std::string
& password
) {
218 if (!AddUserToSession(gaia_id
, username
, password
))
221 if (const user_manager::User
* active_user
=
222 user_manager::UserManager::Get()->GetActiveUser()) {
223 return active_user
->email() == username
;
229 user_manager::User::OAuthTokenStatus
GetOAuthStatusFromLocalState(
230 const std::string
& account_id
) const {
231 PrefService
* local_state
= g_browser_process
->local_state();
232 const base::DictionaryValue
* prefs_oauth_status
=
233 local_state
->GetDictionary("OAuthTokenStatus");
234 int oauth_token_status
= user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN
;
235 if (prefs_oauth_status
&&
236 prefs_oauth_status
->GetIntegerWithoutPathExpansion(
237 account_id
, &oauth_token_status
)) {
238 user_manager::User::OAuthTokenStatus result
=
239 static_cast<user_manager::User::OAuthTokenStatus
>(oauth_token_status
);
242 return user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN
;
246 // OobeBaseTest overrides.
247 Profile
* profile() override
{
248 if (user_manager::UserManager::Get()->GetActiveUser())
249 return ProfileManager::GetPrimaryUserProfile();
251 return OobeBaseTest::profile();
254 bool AddUserToSession(const std::string
& gaia_id
,
255 const std::string
& username
,
256 const std::string
& password
) {
257 ExistingUserController
* controller
=
258 ExistingUserController::current_controller();
264 UserContext
user_context(username
);
265 user_context
.SetGaiaID(gaia_id
);
266 user_context
.SetKey(Key(password
));
267 controller
->Login(user_context
, SigninSpecifics());
268 content::WindowedNotificationObserver(
269 chrome::NOTIFICATION_SESSION_STARTED
,
270 content::NotificationService::AllSources()).Wait();
271 const user_manager::UserList
& logged_users
=
272 user_manager::UserManager::Get()->GetLoggedInUsers();
273 for (user_manager::UserList::const_iterator it
= logged_users
.begin();
274 it
!= logged_users
.end();
276 if ((*it
)->email() == username
)
282 void SetupGaiaServerWithAccessTokens() {
283 fake_gaia_
->MapEmailToGaiaId(kTestEmail
, kTestGaiaId
);
285 // Configure OAuth authentication.
286 GaiaUrls
* gaia_urls
= GaiaUrls::GetInstance();
288 // This token satisfies the userinfo.email request from
289 // DeviceOAuth2TokenService used in token validation.
290 FakeGaia::AccessTokenInfo userinfo_token_info
;
291 userinfo_token_info
.token
= kTestUserinfoToken
;
292 userinfo_token_info
.scopes
.insert(
293 "https://www.googleapis.com/auth/userinfo.email");
294 userinfo_token_info
.audience
= gaia_urls
->oauth2_chrome_client_id();
295 userinfo_token_info
.email
= kTestEmail
;
296 fake_gaia_
->IssueOAuthToken(kTestRefreshToken
, userinfo_token_info
);
298 FakeGaia::AccessTokenInfo userinfo_profile_token_info
;
299 userinfo_profile_token_info
.token
= kTestUserinfoToken
;
300 userinfo_profile_token_info
.scopes
.insert(
301 "https://www.googleapis.com/auth/userinfo.profile");
302 userinfo_profile_token_info
.audience
= gaia_urls
->oauth2_chrome_client_id();
303 userinfo_profile_token_info
.email
= kTestEmail
;
304 fake_gaia_
->IssueOAuthToken(kTestRefreshToken
, userinfo_profile_token_info
);
306 // The any-api access token for accessing the token minting endpoint.
307 FakeGaia::AccessTokenInfo login_token_info
;
308 login_token_info
.token
= kTestLoginToken
;
309 login_token_info
.scopes
.insert(GaiaConstants::kAnyApiOAuth2Scope
);
310 login_token_info
.audience
= gaia_urls
->oauth2_chrome_client_id();
311 fake_gaia_
->IssueOAuthToken(kTestRefreshToken
, login_token_info
);
313 // The /auth/chromesync access token for accessing sync endpoint.
314 FakeGaia::AccessTokenInfo sync_token_info
;
315 sync_token_info
.token
= kTestSyncToken
;
316 sync_token_info
.scopes
.insert(GaiaConstants::kChromeSyncOAuth2Scope
);
317 sync_token_info
.audience
= gaia_urls
->oauth2_chrome_client_id();
318 fake_gaia_
->IssueOAuthToken(kTestRefreshToken
, sync_token_info
);
320 FakeGaia::AccessTokenInfo auth_login_token_info
;
321 auth_login_token_info
.token
= kTestAuthLoginToken
;
322 auth_login_token_info
.scopes
.insert(GaiaConstants::kOAuth1LoginScope
);
323 auth_login_token_info
.audience
= gaia_urls
->oauth2_chrome_client_id();
324 fake_gaia_
->IssueOAuthToken(kTestRefreshToken
, auth_login_token_info
);
327 void CheckSessionState(OAuth2LoginManager::SessionRestoreState state
) {
328 OAuth2LoginManager
* login_manager
=
329 OAuth2LoginManagerFactory::GetInstance()->GetForProfile(
331 ASSERT_EQ(state
, login_manager
->state());
334 void WaitForMergeSessionCompletion(
335 OAuth2LoginManager::SessionRestoreState final_state
) {
336 // Wait for the session merge to finish.
337 std::set
<OAuth2LoginManager::SessionRestoreState
> states
;
338 states
.insert(OAuth2LoginManager::SESSION_RESTORE_DONE
);
339 states
.insert(OAuth2LoginManager::SESSION_RESTORE_FAILED
);
340 states
.insert(OAuth2LoginManager::SESSION_RESTORE_CONNECTION_FAILED
);
341 OAuth2LoginManagerStateWaiter
merge_session_waiter(profile());
342 merge_session_waiter
.WaitForStates(states
);
343 EXPECT_EQ(merge_session_waiter
.final_state(), final_state
);
346 void StartNewUserSession(bool wait_for_merge
) {
347 SetupGaiaServerForNewAccount();
348 SimulateNetworkOnline();
349 WaitForGaiaPageLoad();
351 content::WindowedNotificationObserver
session_start_waiter(
352 chrome::NOTIFICATION_SESSION_STARTED
,
353 content::NotificationService::AllSources());
355 // Use capitalized and dotted user name on purpose to make sure
356 // our email normalization kicks in.
357 GetLoginDisplay()->ShowSigninScreenForCreds(kTestRawEmail
,
358 kTestAccountPassword
);
359 session_start_waiter
.Wait();
361 if (wait_for_merge
) {
362 // Wait for the session merge to finish.
363 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_DONE
);
367 DISALLOW_COPY_AND_ASSIGN(OAuth2Test
);
370 class CookieReader
: public base::RefCountedThreadSafe
<CookieReader
> {
375 void ReadCookies(Profile
* profile
) {
376 context_
= profile
->GetRequestContext();
377 content::BrowserThread::PostTask(
378 content::BrowserThread::IO
, FROM_HERE
,
379 base::Bind(&CookieReader::ReadCookiesOnIOThread
,
381 runner_
= new content::MessageLoopRunner
;
385 std::string
GetCookieValue(const std::string
& name
) {
386 for (std::vector
<net::CanonicalCookie
>::const_iterator iter
=
387 cookie_list_
.begin();
388 iter
!= cookie_list_
.end();
390 if (iter
->Name() == name
) {
391 return iter
->Value();
394 return std::string();
398 friend class base::RefCountedThreadSafe
<CookieReader
>;
400 virtual ~CookieReader() {
403 void ReadCookiesOnIOThread() {
404 context_
->GetURLRequestContext()->cookie_store()->GetCookieMonster()->
405 GetAllCookiesAsync(base::Bind(
406 &CookieReader::OnGetAllCookiesOnUIThread
,
410 void OnGetAllCookiesOnUIThread(const net::CookieList
& cookies
) {
411 cookie_list_
= cookies
;
412 content::BrowserThread::PostTask(
413 content::BrowserThread::UI
, FROM_HERE
,
414 base::Bind(&CookieReader::OnCookiesReadyOnUIThread
,
418 void OnCookiesReadyOnUIThread() {
422 scoped_refptr
<net::URLRequestContextGetter
> context_
;
423 net::CookieList cookie_list_
;
424 scoped_refptr
<content::MessageLoopRunner
> runner_
;
426 DISALLOW_COPY_AND_ASSIGN(CookieReader
);
429 // PRE_MergeSession is testing merge session for a new profile.
430 IN_PROC_BROWSER_TEST_F(OAuth2Test
, PRE_PRE_PRE_MergeSession
) {
431 StartNewUserSession(true);
432 // Check for existance of refresh token.
433 std::string account_id
= PickAccountId(profile(), kTestGaiaId
, kTestEmail
);
434 ProfileOAuth2TokenService
* token_service
=
435 ProfileOAuth2TokenServiceFactory::GetForProfile(
437 EXPECT_TRUE(token_service
->RefreshTokenIsAvailable(account_id
));
439 EXPECT_EQ(GetOAuthStatusFromLocalState(account_id
),
440 user_manager::User::OAUTH2_TOKEN_STATUS_VALID
);
441 scoped_refptr
<CookieReader
> cookie_reader(new CookieReader());
442 cookie_reader
->ReadCookies(profile());
443 EXPECT_EQ(cookie_reader
->GetCookieValue("SID"), kTestSessionSIDCookie
);
444 EXPECT_EQ(cookie_reader
->GetCookieValue("LSID"), kTestSessionLSIDCookie
);
447 // MergeSession test is running merge session process for an existing profile
448 // that was generated in PRE_PRE_PRE_MergeSession test. In this test, we
449 // are not running /MergeSession process since the /ListAccounts call confirms
450 // that the session is not stale.
451 IN_PROC_BROWSER_TEST_F(OAuth2Test
, PRE_PRE_MergeSession
) {
452 SetupGaiaServerForUnexpiredAccount();
453 SimulateNetworkOnline();
454 LoginAsExistingUser();
455 scoped_refptr
<CookieReader
> cookie_reader(new CookieReader());
456 cookie_reader
->ReadCookies(profile());
457 // These are still cookie values form the initial session since
459 EXPECT_EQ(cookie_reader
->GetCookieValue("SID"), kTestSessionSIDCookie
);
460 EXPECT_EQ(cookie_reader
->GetCookieValue("LSID"), kTestSessionLSIDCookie
);
463 // MergeSession test is running merge session process for an existing profile
464 // that was generated in PRE_PRE_MergeSession test.
465 // Disabled due to flakiness: crbug.com/496832
466 IN_PROC_BROWSER_TEST_F(OAuth2Test
, DISABLED_PRE_MergeSession
) {
467 SetupGaiaServerForExpiredAccount();
468 SimulateNetworkOnline();
469 LoginAsExistingUser();
470 scoped_refptr
<CookieReader
> cookie_reader(new CookieReader());
471 cookie_reader
->ReadCookies(profile());
472 // These should be cookie values that we generated by calling /MergeSession,
473 // since /ListAccounts should have tell us that the initial session cookies
475 EXPECT_EQ(cookie_reader
->GetCookieValue("SID"), kTestSession2SIDCookie
);
476 EXPECT_EQ(cookie_reader
->GetCookieValue("LSID"), kTestSession2LSIDCookie
);
479 // MergeSession test is attempting to merge session for an existing profile
480 // that was generated in PRE_PRE_MergeSession test. This attempt should fail
481 // since FakeGaia instance isn't configured to return relevant tokens/cookies.
482 // Disabled due to flakiness: crbug.com/496832
483 IN_PROC_BROWSER_TEST_F(OAuth2Test
, DISABLED_MergeSession
) {
484 SimulateNetworkOnline();
486 content::WindowedNotificationObserver(
487 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
,
488 content::NotificationService::AllSources()).Wait();
490 JsExpect("!!document.querySelector('#account-picker')");
491 JsExpect("!!document.querySelector('#pod-row')");
493 std::string account_id
= PickAccountId(profile(), kTestGaiaId
, kTestEmail
);
494 EXPECT_EQ(GetOAuthStatusFromLocalState(account_id
),
495 user_manager::User::OAUTH2_TOKEN_STATUS_VALID
);
497 EXPECT_TRUE(TryToLogin(kTestGaiaId
, kTestEmail
, kTestAccountPassword
));
499 // Wait for the session merge to finish.
500 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_FAILED
);
502 EXPECT_EQ(GetOAuthStatusFromLocalState(account_id
),
503 user_manager::User::OAUTH2_TOKEN_STATUS_INVALID
);
507 const char kGooglePageContent
[] =
508 "<html><title>Hello!</title><script>alert('hello');</script>"
509 "<body>Hello Google!</body></html>";
510 const char kRandomPageContent
[] =
511 "<html><title>SomthingElse</title><body>I am SomethingElse</body></html>";
512 const char kHelloPagePath
[] = "/hello_google";
513 const char kRandomPagePath
[] = "/non_google_page";
516 // FakeGoogle serves content of http://www.google.com/hello_google page for
517 // merge session tests.
520 FakeGoogle() : start_event_(true, false) {
525 scoped_ptr
<HttpResponse
> HandleRequest(const HttpRequest
& request
) {
526 // The scheme and host of the URL is actually not important but required to
527 // get a valid GURL in order to parse |request.relative_url|.
528 GURL request_url
= GURL("http://localhost").Resolve(request
.relative_url
);
529 LOG(WARNING
) << "Requesting page " << request
.relative_url
;
530 std::string request_path
= request_url
.path();
531 scoped_ptr
<BasicHttpResponse
> http_response(new BasicHttpResponse());
532 if (request_path
== kHelloPagePath
) { // Serving "google" page.
533 start_event_
.Signal();
534 content::BrowserThread::PostTask(
535 content::BrowserThread::UI
, FROM_HERE
,
536 base::Bind(&FakeGoogle::QuitRunnerOnUIThread
,
537 base::Unretained(this)));
539 http_response
->set_code(net::HTTP_OK
);
540 http_response
->set_content_type("text/html");
541 http_response
->set_content(kGooglePageContent
);
542 } else if (request_path
== kRandomPagePath
) { // Serving "non-google" page.
543 http_response
->set_code(net::HTTP_OK
);
544 http_response
->set_content_type("text/html");
545 http_response
->set_content(kRandomPageContent
);
547 return scoped_ptr
<HttpResponse
>(); // Request not understood.
550 return http_response
.Pass();
553 // True if we have already served the test page.
554 bool IsPageRequested() { return start_event_
.IsSignaled(); }
556 // Waits until we receive a request to serve the test page.
557 void WaitForPageRequest() {
558 // If we have already served the request, bail out.
559 if (start_event_
.IsSignaled())
562 runner_
= new content::MessageLoopRunner
;
567 void QuitRunnerOnUIThread() {
571 // This event will tell us when we actually see HTTP request on the server
572 // side. It should be signalled only after the page/XHR throttle had been
573 // removed (after merge session completes).
574 base::WaitableEvent start_event_
;
575 scoped_refptr
<content::MessageLoopRunner
> runner_
;
577 DISALLOW_COPY_AND_ASSIGN(FakeGoogle
);
580 // FakeGaia specialization that can delay /MergeSession handler until
581 // we explicitly call DelayedFakeGaia::UnblockMergeSession().
582 class DelayedFakeGaia
: public FakeGaia
{
585 : blocking_event_(true, false),
586 start_event_(true, false) {
589 void UnblockMergeSession() {
590 blocking_event_
.Signal();
593 void WaitForMergeSessionToStart() {
594 // If we have already served the request, bail out.
595 if (start_event_
.IsSignaled())
598 runner_
= new content::MessageLoopRunner
;
603 // FakeGaia overrides.
604 void HandleMergeSession(const HttpRequest
& request
,
605 BasicHttpResponse
* http_response
) override
{
606 start_event_
.Signal();
607 content::BrowserThread::PostTask(
608 content::BrowserThread::UI
, FROM_HERE
,
609 base::Bind(&DelayedFakeGaia::QuitRunnerOnUIThread
,
610 base::Unretained(this)));
611 blocking_event_
.Wait();
612 FakeGaia::HandleMergeSession(request
, http_response
);
615 void QuitRunnerOnUIThread() {
620 base::WaitableEvent blocking_event_
;
621 base::WaitableEvent start_event_
;
622 scoped_refptr
<content::MessageLoopRunner
> runner_
;
624 DISALLOW_COPY_AND_ASSIGN(DelayedFakeGaia
);
627 class MergeSessionTest
: public OAuth2Test
{
629 MergeSessionTest() : delayed_fake_gaia_(new DelayedFakeGaia()) {
630 fake_gaia_
.reset(delayed_fake_gaia_
);
633 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
634 OAuth2Test::SetUpCommandLine(command_line
);
636 // Get fake URL for fake google.com.
637 const GURL
& server_url
= embedded_test_server()->base_url();
638 GURL::Replacements replace_google_host
;
639 replace_google_host
.SetHostStr("www.google.com");
640 GURL google_url
= server_url
.ReplaceComponents(replace_google_host
);
641 fake_google_page_url_
= google_url
.Resolve(kHelloPagePath
);
643 GURL::Replacements replace_non_google_host
;
644 replace_non_google_host
.SetHostStr("www.somethingelse.org");
645 GURL non_google_url
= server_url
.ReplaceComponents(replace_non_google_host
);
646 non_google_page_url_
= non_google_url
.Resolve(kRandomPagePath
);
649 void SetUp() override
{
650 embedded_test_server()->RegisterRequestHandler(
651 base::Bind(&FakeGoogle::HandleRequest
,
652 base::Unretained(&fake_google_
)));
657 void UnblockMergeSession() {
658 delayed_fake_gaia_
->UnblockMergeSession();
661 void WaitForMergeSessionToStart() {
662 delayed_fake_gaia_
->WaitForMergeSessionToStart();
665 void JsExpect(content::WebContents
* contents
,
666 const std::string
& expression
) {
668 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
670 "window.domAutomationController.send(!!(" + expression
+ "));",
672 ASSERT_TRUE(result
) << expression
;
675 const GURL
& GetBackGroundPageUrl(const std::string
& extension_id
) {
676 extensions::ProcessManager
* manager
=
677 extensions::ProcessManager::Get(profile());
678 extensions::ExtensionHost
* host
=
679 manager
->GetBackgroundHostForExtension(extension_id
);
680 return host
->host_contents()->GetURL();
683 void JsExpectOnBackgroundPage(const std::string
& extension_id
,
684 const std::string
& expression
) {
685 extensions::ProcessManager
* manager
=
686 extensions::ProcessManager::Get(profile());
687 extensions::ExtensionHost
* host
=
688 manager
->GetBackgroundHostForExtension(extension_id
);
690 ADD_FAILURE() << "Extension " << extension_id
691 << " has no background page.";
695 JsExpect(host
->host_contents(), expression
);
698 FakeGoogle fake_google_
;
699 DelayedFakeGaia
* delayed_fake_gaia_
;
700 GURL fake_google_page_url_
;
701 GURL non_google_page_url_
;
704 DISALLOW_COPY_AND_ASSIGN(MergeSessionTest
);
707 Browser
* FindOrCreateVisibleBrowser(Profile
* profile
) {
708 chrome::ScopedTabbedBrowserDisplayer
displayer(
709 profile
, chrome::GetActiveDesktop());
710 Browser
* browser
= displayer
.browser();
711 if (browser
->tab_strip_model()->count() == 0)
712 chrome::AddTabAt(browser
, GURL(), -1, true);
716 IN_PROC_BROWSER_TEST_F(MergeSessionTest
, PageThrottle
) {
717 StartNewUserSession(false);
719 // Try to open a page from google.com.
721 FindOrCreateVisibleBrowser(profile());
722 ui_test_utils::NavigateToURLWithDisposition(
724 fake_google_page_url_
,
725 CURRENT_TAB
, ui_test_utils::BROWSER_TEST_NONE
);
727 // Wait until we get send merge session request.
728 WaitForMergeSessionToStart();
730 // Make sure the page is blocked by the throttle.
731 EXPECT_FALSE(fake_google_
.IsPageRequested());
733 // Check that throttle page is displayed instead.
734 base::string16 title
;
735 ui_test_utils::GetCurrentTabTitle(browser
, &title
);
736 DVLOG(1) << "Loaded page at the start : " << title
;
738 // Unblock GAIA request.
739 UnblockMergeSession();
741 // Wait for the session merge to finish.
742 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_DONE
);
744 // Make sure the test page is served.
745 fake_google_
.WaitForPageRequest();
747 // Check that real page is no longer blocked by the throttle and that the
748 // real page pops up JS dialog.
749 AppModalDialog
* dialog
= ui_test_utils::WaitForAppModalDialog();
750 ASSERT_TRUE(dialog
->IsJavaScriptModalDialog());
751 JavaScriptAppModalDialog
* js_dialog
=
752 static_cast<JavaScriptAppModalDialog
*>(dialog
);
753 js_dialog
->native_dialog()->AcceptAppModalDialog();
755 ui_test_utils::GetCurrentTabTitle(browser
, &title
);
756 DVLOG(1) << "Loaded page at the end : " << title
;
759 IN_PROC_BROWSER_TEST_F(MergeSessionTest
, XHRThrottle
) {
760 StartNewUserSession(false);
762 // Wait until we get send merge session request.
763 WaitForMergeSessionToStart();
765 // Reset ExtensionBrowserTest::observer_ to the right browser object.
766 Browser
* browser
= FindOrCreateVisibleBrowser(profile());
767 observer_
.reset(new ExtensionTestNotificationObserver(browser
));
769 // Run background page tests. The tests will just wait for XHR request
771 extensions::ResultCatcher catcher
;
773 scoped_ptr
<ExtensionTestMessageListener
> non_google_xhr_listener(
774 new ExtensionTestMessageListener("non-google-xhr-received", false));
776 // Load extension with a background page. The background page will
777 // attempt to load |fake_google_page_url_| via XHR.
778 const extensions::Extension
* ext
= LoadExtension(
779 test_data_dir_
.AppendASCII("merge_session"));
782 // Kick off XHR request from the extension.
783 JsExpectOnBackgroundPage(
785 base::StringPrintf("startThrottledTests('%s', '%s')",
786 fake_google_page_url_
.spec().c_str(),
787 non_google_page_url_
.spec().c_str()));
789 // Verify that we've sent XHR request form the extension side...
790 JsExpectOnBackgroundPage(ext
->id(),
791 "googleRequestSent && !googleResponseReceived");
793 // ...but didn't see it on the server side yet.
794 EXPECT_FALSE(fake_google_
.IsPageRequested());
796 // Unblock GAIA request.
797 UnblockMergeSession();
799 // Wait for the session merge to finish.
800 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_DONE
);
802 // Wait until non-google XHR content to load first.
803 ASSERT_TRUE(non_google_xhr_listener
->WaitUntilSatisfied());
805 if (!catcher
.GetNextResult()) {
806 std::string message
= catcher
.message();
807 ADD_FAILURE() << "Tests failed: " << message
;
810 EXPECT_TRUE(fake_google_
.IsPageRequested());
813 } // namespace chromeos