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. 4 5#include <string> 6 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/test/oobe_base_test.h" 17#include "chrome/browser/chromeos/login/wizard_controller.h" 18#include "chrome/browser/profiles/profile_manager.h" 19#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 20#include "chrome/browser/ui/app_modal_dialogs/javascript_app_modal_dialog.h" 21#include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h" 22#include "chrome/browser/ui/browser.h" 23#include "chrome/browser/ui/browser_tabstrip.h" 24#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" 25#include "chrome/browser/ui/tabs/tab_strip_model.h" 26#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.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/signin/core/browser/profile_oauth2_token_service.h" 31#include "components/user_manager/user.h" 32#include "components/user_manager/user_manager.h" 33#include "content/public/browser/notification_service.h" 34#include "content/public/test/browser_test_utils.h" 35#include "extensions/browser/process_manager.h" 36#include "extensions/test/extension_test_message_listener.h" 37#include "extensions/test/result_catcher.h" 38#include "google_apis/gaia/gaia_constants.h" 39#include "google_apis/gaia/gaia_urls.h" 40#include "net/cookies/canonical_cookie.h" 41#include "net/cookies/cookie_monster.h" 42#include "net/cookies/cookie_store.h" 43#include "net/test/embedded_test_server/http_request.h" 44#include "net/test/embedded_test_server/http_response.h" 45#include "net/url_request/url_request_context.h" 46#include "net/url_request/url_request_context_getter.h" 47 48using net::test_server::BasicHttpResponse; 49using net::test_server::HttpRequest; 50using net::test_server::HttpResponse; 51 52namespace chromeos { 53 54namespace { 55 56// Email of owner account for test. 57const char kTestAccountId[] = "username@gmail.com"; 58const char kTestRawAccountId[] = "User.Name"; 59const char kTestAccountPassword[] = "fake-password"; 60const char kTestAuthCode[] = "fake-auth-code"; 61const char kTestGaiaUberToken[] = "fake-uber-token"; 62const char kTestAuthLoginAccessToken[] = "fake-access-token"; 63const char kTestRefreshToken[] = "fake-refresh-token"; 64const char kTestAuthSIDCookie[] = "fake-auth-SID-cookie"; 65const char kTestAuthLSIDCookie[] = "fake-auth-LSID-cookie"; 66const char kTestSessionSIDCookie[] = "fake-session-SID-cookie"; 67const char kTestSessionLSIDCookie[] = "fake-session-LSID-cookie"; 68const char kTestSession2SIDCookie[] = "fake-session2-SID-cookie"; 69const char kTestSession2LSIDCookie[] = "fake-session2-LSID-cookie"; 70const char kTestUserinfoToken[] = "fake-userinfo-token"; 71const char kTestLoginToken[] = "fake-login-token"; 72const char kTestSyncToken[] = "fake-sync-token"; 73const char kTestAuthLoginToken[] = "fake-oauthlogin-token"; 74 75class OAuth2LoginManagerStateWaiter : public OAuth2LoginManager::Observer { 76 public: 77 explicit OAuth2LoginManagerStateWaiter(Profile* profile) 78 : profile_(profile), 79 waiting_for_state_(false), 80 final_state_(OAuth2LoginManager::SESSION_RESTORE_NOT_STARTED) { 81 } 82 83 void WaitForStates( 84 const std::set<OAuth2LoginManager::SessionRestoreState>& states) { 85 DCHECK(!waiting_for_state_); 86 OAuth2LoginManager* login_manager = 87 OAuth2LoginManagerFactory::GetInstance()->GetForProfile(profile_); 88 states_ = states; 89 if (states_.find(login_manager->state()) != states_.end()) { 90 final_state_ = login_manager->state(); 91 return; 92 } 93 94 waiting_for_state_ = true; 95 login_manager->AddObserver(this); 96 runner_ = new content::MessageLoopRunner; 97 runner_->Run(); 98 login_manager->RemoveObserver(this); 99 } 100 101 OAuth2LoginManager::SessionRestoreState final_state() { return final_state_; } 102 103 private: 104 // OAuth2LoginManager::Observer overrides. 105 virtual void OnSessionRestoreStateChanged( 106 Profile* user_profile, 107 OAuth2LoginManager::SessionRestoreState state) OVERRIDE { 108 if (!waiting_for_state_) 109 return; 110 111 if (states_.find(state) == states_.end()) 112 return; 113 114 final_state_ = state; 115 waiting_for_state_ = false; 116 runner_->Quit(); 117 } 118 119 Profile* profile_; 120 std::set<OAuth2LoginManager::SessionRestoreState> states_; 121 bool waiting_for_state_; 122 OAuth2LoginManager::SessionRestoreState final_state_; 123 scoped_refptr<content::MessageLoopRunner> runner_; 124 125 DISALLOW_COPY_AND_ASSIGN(OAuth2LoginManagerStateWaiter); 126}; 127 128} // namespace 129 130class OAuth2Test : public OobeBaseTest { 131 protected: 132 OAuth2Test() {} 133 134 void SetupGaiaServerForNewAccount() { 135 FakeGaia::MergeSessionParams params; 136 params.auth_sid_cookie = kTestAuthSIDCookie; 137 params.auth_lsid_cookie = kTestAuthLSIDCookie; 138 params.auth_code = kTestAuthCode; 139 params.refresh_token = kTestRefreshToken; 140 params.access_token = kTestAuthLoginAccessToken; 141 params.gaia_uber_token = kTestGaiaUberToken; 142 params.session_sid_cookie = kTestSessionSIDCookie; 143 params.session_lsid_cookie = kTestSessionLSIDCookie; 144 fake_gaia_->SetMergeSessionParams(params); 145 SetupGaiaServerWithAccessTokens(); 146 } 147 148 void SetupGaiaServerForUnexpiredAccount() { 149 FakeGaia::MergeSessionParams params; 150 params.email = kTestAccountId; 151 fake_gaia_->SetMergeSessionParams(params); 152 SetupGaiaServerWithAccessTokens(); 153 } 154 155 void SetupGaiaServerForExpiredAccount() { 156 FakeGaia::MergeSessionParams params; 157 params.gaia_uber_token = kTestGaiaUberToken; 158 params.session_sid_cookie = kTestSession2SIDCookie; 159 params.session_lsid_cookie = kTestSession2LSIDCookie; 160 fake_gaia_->SetMergeSessionParams(params); 161 SetupGaiaServerWithAccessTokens(); 162 } 163 164 void LoginAsExistingUser() { 165 content::WindowedNotificationObserver( 166 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, 167 content::NotificationService::AllSources()).Wait(); 168 169 JsExpect("!!document.querySelector('#account-picker')"); 170 JsExpect("!!document.querySelector('#pod-row')"); 171 172 EXPECT_EQ(GetOAuthStatusFromLocalState(kTestAccountId), 173 user_manager::User::OAUTH2_TOKEN_STATUS_VALID); 174 175 EXPECT_TRUE(TryToLogin(kTestAccountId, kTestAccountPassword)); 176 Profile* profile = ProfileManager::GetPrimaryUserProfile(); 177 178 // Wait for the session merge to finish. 179 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_DONE); 180 181 // Check for existance of refresh token. 182 ProfileOAuth2TokenService* token_service = 183 ProfileOAuth2TokenServiceFactory::GetForProfile(profile); 184 EXPECT_TRUE(token_service->RefreshTokenIsAvailable(kTestAccountId)); 185 186 EXPECT_EQ(GetOAuthStatusFromLocalState(kTestAccountId), 187 user_manager::User::OAUTH2_TOKEN_STATUS_VALID); 188 } 189 190 bool TryToLogin(const std::string& username, 191 const std::string& password) { 192 if (!AddUserToSession(username, password)) 193 return false; 194 195 if (const user_manager::User* active_user = 196 user_manager::UserManager::Get()->GetActiveUser()) { 197 return active_user->email() == username; 198 } 199 200 return false; 201 } 202 203 user_manager::User::OAuthTokenStatus GetOAuthStatusFromLocalState( 204 const std::string& user_id) const { 205 PrefService* local_state = g_browser_process->local_state(); 206 const base::DictionaryValue* prefs_oauth_status = 207 local_state->GetDictionary("OAuthTokenStatus"); 208 int oauth_token_status = user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN; 209 if (prefs_oauth_status && 210 prefs_oauth_status->GetIntegerWithoutPathExpansion( 211 user_id, &oauth_token_status)) { 212 user_manager::User::OAuthTokenStatus result = 213 static_cast<user_manager::User::OAuthTokenStatus>(oauth_token_status); 214 return result; 215 } 216 return user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN; 217 } 218 219 protected: 220 // OobeBaseTest overrides. 221 virtual Profile* profile() OVERRIDE { 222 if (user_manager::UserManager::Get()->GetActiveUser()) 223 return ProfileManager::GetPrimaryUserProfile(); 224 225 return OobeBaseTest::profile(); 226 } 227 228 bool AddUserToSession(const std::string& username, 229 const std::string& password) { 230 ExistingUserController* controller = 231 ExistingUserController::current_controller(); 232 if (!controller) { 233 ADD_FAILURE(); 234 return false; 235 } 236 237 UserContext user_context(username); 238 user_context.SetKey(Key(password)); 239 controller->Login(user_context, SigninSpecifics()); 240 content::WindowedNotificationObserver( 241 chrome::NOTIFICATION_SESSION_STARTED, 242 content::NotificationService::AllSources()).Wait(); 243 const user_manager::UserList& logged_users = 244 user_manager::UserManager::Get()->GetLoggedInUsers(); 245 for (user_manager::UserList::const_iterator it = logged_users.begin(); 246 it != logged_users.end(); 247 ++it) { 248 if ((*it)->email() == username) 249 return true; 250 } 251 return false; 252 } 253 254 void SetupGaiaServerWithAccessTokens() { 255 // Configure OAuth authentication. 256 GaiaUrls* gaia_urls = GaiaUrls::GetInstance(); 257 258 // This token satisfies the userinfo.email request from 259 // DeviceOAuth2TokenService used in token validation. 260 FakeGaia::AccessTokenInfo userinfo_token_info; 261 userinfo_token_info.token = kTestUserinfoToken; 262 userinfo_token_info.scopes.insert( 263 "https://www.googleapis.com/auth/userinfo.email"); 264 userinfo_token_info.audience = gaia_urls->oauth2_chrome_client_id(); 265 userinfo_token_info.email = kTestAccountId; 266 fake_gaia_->IssueOAuthToken(kTestRefreshToken, userinfo_token_info); 267 268 FakeGaia::AccessTokenInfo userinfo_profile_token_info; 269 userinfo_profile_token_info.token = kTestUserinfoToken; 270 userinfo_profile_token_info.scopes.insert( 271 "https://www.googleapis.com/auth/userinfo.profile"); 272 userinfo_profile_token_info.audience = gaia_urls->oauth2_chrome_client_id(); 273 userinfo_profile_token_info.email = kTestAccountId; 274 fake_gaia_->IssueOAuthToken(kTestRefreshToken, userinfo_profile_token_info); 275 276 // The any-api access token for accessing the token minting endpoint. 277 FakeGaia::AccessTokenInfo login_token_info; 278 login_token_info.token = kTestLoginToken; 279 login_token_info.scopes.insert(GaiaConstants::kAnyApiOAuth2Scope); 280 login_token_info.audience = gaia_urls->oauth2_chrome_client_id(); 281 fake_gaia_->IssueOAuthToken(kTestRefreshToken, login_token_info); 282 283 // The /auth/chromesync access token for accessing sync endpoint. 284 FakeGaia::AccessTokenInfo sync_token_info; 285 sync_token_info.token = kTestSyncToken; 286 sync_token_info.scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope); 287 sync_token_info.audience = gaia_urls->oauth2_chrome_client_id(); 288 fake_gaia_->IssueOAuthToken(kTestRefreshToken, sync_token_info); 289 290 FakeGaia::AccessTokenInfo auth_login_token_info; 291 auth_login_token_info.token = kTestAuthLoginToken; 292 auth_login_token_info.scopes.insert(GaiaConstants::kOAuth1LoginScope); 293 auth_login_token_info.audience = gaia_urls->oauth2_chrome_client_id(); 294 fake_gaia_->IssueOAuthToken(kTestRefreshToken, auth_login_token_info); 295 } 296 297 void CheckSessionState(OAuth2LoginManager::SessionRestoreState state) { 298 OAuth2LoginManager* login_manager = 299 OAuth2LoginManagerFactory::GetInstance()->GetForProfile( 300 profile()); 301 ASSERT_EQ(state, login_manager->state()); 302 } 303 304 void WaitForMergeSessionCompletion( 305 OAuth2LoginManager::SessionRestoreState final_state) { 306 // Wait for the session merge to finish. 307 std::set<OAuth2LoginManager::SessionRestoreState> states; 308 states.insert(OAuth2LoginManager::SESSION_RESTORE_DONE); 309 states.insert(OAuth2LoginManager::SESSION_RESTORE_FAILED); 310 states.insert(OAuth2LoginManager::SESSION_RESTORE_CONNECTION_FAILED); 311 OAuth2LoginManagerStateWaiter merge_session_waiter(profile()); 312 merge_session_waiter.WaitForStates(states); 313 EXPECT_EQ(merge_session_waiter.final_state(), final_state); 314 } 315 316 void StartNewUserSession(bool wait_for_merge) { 317 SetupGaiaServerForNewAccount(); 318 SimulateNetworkOnline(); 319 chromeos::WizardController::SkipPostLoginScreensForTesting(); 320 chromeos::WizardController* wizard_controller = 321 chromeos::WizardController::default_controller(); 322 wizard_controller->SkipToLoginForTesting(LoginScreenContext()); 323 324 content::WindowedNotificationObserver( 325 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, 326 content::NotificationService::AllSources()).Wait(); 327 328 // Use capitalized and dotted user name on purpose to make sure 329 // our email normalization kicks in. 330 GetLoginDisplay()->ShowSigninScreenForCreds(kTestRawAccountId, 331 kTestAccountPassword); 332 333 content::WindowedNotificationObserver( 334 chrome::NOTIFICATION_SESSION_STARTED, 335 content::NotificationService::AllSources()).Wait(); 336 337 if (wait_for_merge) { 338 // Wait for the session merge to finish. 339 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_DONE); 340 } 341} 342 343 DISALLOW_COPY_AND_ASSIGN(OAuth2Test); 344}; 345 346class CookieReader : public base::RefCountedThreadSafe<CookieReader> { 347 public: 348 CookieReader() { 349 } 350 351 void ReadCookies(Profile* profile) { 352 context_ = profile->GetRequestContext(); 353 content::BrowserThread::PostTask( 354 content::BrowserThread::IO, FROM_HERE, 355 base::Bind(&CookieReader::ReadCookiesOnIOThread, 356 this)); 357 runner_ = new content::MessageLoopRunner; 358 runner_->Run(); 359 } 360 361 std::string GetCookieValue(const std::string& name) { 362 for (std::vector<net::CanonicalCookie>::const_iterator iter = 363 cookie_list_.begin(); 364 iter != cookie_list_.end(); 365 ++iter) { 366 if (iter->Name() == name) { 367 return iter->Value(); 368 } 369 } 370 return std::string(); 371 } 372 373 private: 374 friend class base::RefCountedThreadSafe<CookieReader>; 375 376 virtual ~CookieReader() { 377 } 378 379 void ReadCookiesOnIOThread() { 380 context_->GetURLRequestContext()->cookie_store()->GetCookieMonster()-> 381 GetAllCookiesAsync(base::Bind( 382 &CookieReader::OnGetAllCookiesOnUIThread, 383 this)); 384 } 385 386 void OnGetAllCookiesOnUIThread(const net::CookieList& cookies) { 387 cookie_list_ = cookies; 388 content::BrowserThread::PostTask( 389 content::BrowserThread::UI, FROM_HERE, 390 base::Bind(&CookieReader::OnCookiesReadyOnUIThread, 391 this)); 392 } 393 394 void OnCookiesReadyOnUIThread() { 395 runner_->Quit(); 396 } 397 398 scoped_refptr<net::URLRequestContextGetter> context_; 399 net::CookieList cookie_list_; 400 scoped_refptr<content::MessageLoopRunner> runner_; 401 402 DISALLOW_COPY_AND_ASSIGN(CookieReader); 403}; 404 405// PRE_MergeSession is testing merge session for a new profile. 406IN_PROC_BROWSER_TEST_F(OAuth2Test, PRE_PRE_PRE_MergeSession) { 407 StartNewUserSession(true); 408 // Check for existance of refresh token. 409 ProfileOAuth2TokenService* token_service = 410 ProfileOAuth2TokenServiceFactory::GetForProfile( 411 profile()); 412 EXPECT_TRUE(token_service->RefreshTokenIsAvailable(kTestAccountId)); 413 414 EXPECT_EQ(GetOAuthStatusFromLocalState(kTestAccountId), 415 user_manager::User::OAUTH2_TOKEN_STATUS_VALID); 416 417 scoped_refptr<CookieReader> cookie_reader(new CookieReader()); 418 cookie_reader->ReadCookies(profile()); 419 EXPECT_EQ(cookie_reader->GetCookieValue("SID"), kTestSessionSIDCookie); 420 EXPECT_EQ(cookie_reader->GetCookieValue("LSID"), kTestSessionLSIDCookie); 421} 422 423// MergeSession test is running merge session process for an existing profile 424// that was generated in PRE_PRE_PRE_MergeSession test. In this test, we 425// are not running /MergeSession process since the /ListAccounts call confirms 426// that the session is not stale. 427IN_PROC_BROWSER_TEST_F(OAuth2Test, PRE_PRE_MergeSession) { 428 SetupGaiaServerForUnexpiredAccount(); 429 SimulateNetworkOnline(); 430 LoginAsExistingUser(); 431 scoped_refptr<CookieReader> cookie_reader(new CookieReader()); 432 cookie_reader->ReadCookies(profile()); 433 // These are still cookie values form the initial session since 434 // /ListAccounts 435 EXPECT_EQ(cookie_reader->GetCookieValue("SID"), kTestSessionSIDCookie); 436 EXPECT_EQ(cookie_reader->GetCookieValue("LSID"), kTestSessionLSIDCookie); 437} 438 439// MergeSession test is running merge session process for an existing profile 440// that was generated in PRE_PRE_MergeSession test. 441IN_PROC_BROWSER_TEST_F(OAuth2Test, PRE_MergeSession) { 442 SetupGaiaServerForExpiredAccount(); 443 SimulateNetworkOnline(); 444 LoginAsExistingUser(); 445 scoped_refptr<CookieReader> cookie_reader(new CookieReader()); 446 cookie_reader->ReadCookies(profile()); 447 // These should be cookie values that we generated by calling /MergeSession, 448 // since /ListAccounts should have tell us that the initial session cookies 449 // are stale. 450 EXPECT_EQ(cookie_reader->GetCookieValue("SID"), kTestSession2SIDCookie); 451 EXPECT_EQ(cookie_reader->GetCookieValue("LSID"), kTestSession2LSIDCookie); 452} 453 454// MergeSession test is attempting to merge session for an existing profile 455// that was generated in PRE_PRE_MergeSession test. This attempt should fail 456// since FakeGaia instance isn't configured to return relevant tokens/cookies. 457// This test is flaky, see: crbug.com/408867. 458IN_PROC_BROWSER_TEST_F(OAuth2Test, DISABLED_MergeSession) { 459 SimulateNetworkOnline(); 460 461 content::WindowedNotificationObserver( 462 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, 463 content::NotificationService::AllSources()).Wait(); 464 465 JsExpect("!!document.querySelector('#account-picker')"); 466 JsExpect("!!document.querySelector('#pod-row')"); 467 468 EXPECT_EQ(GetOAuthStatusFromLocalState(kTestAccountId), 469 user_manager::User::OAUTH2_TOKEN_STATUS_VALID); 470 471 EXPECT_TRUE(TryToLogin(kTestAccountId, kTestAccountPassword)); 472 473 // Wait for the session merge to finish. 474 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_FAILED); 475 476 EXPECT_EQ(GetOAuthStatusFromLocalState(kTestAccountId), 477 user_manager::User::OAUTH2_TOKEN_STATUS_INVALID); 478} 479 480 481const char kGooglePageContent[] = 482 "<html><title>Hello!</title><script>alert('hello');</script>" 483 "<body>Hello Google!</body></html>"; 484const char kRandomPageContent[] = 485 "<html><title>SomthingElse</title><body>I am SomethingElse</body></html>"; 486const char kHelloPagePath[] = "/hello_google"; 487const char kRandomPagePath[] = "/non_google_page"; 488 489 490// FakeGoogle serves content of http://www.google.com/hello_google page for 491// merge session tests. 492class FakeGoogle { 493 public: 494 FakeGoogle() : start_event_(true, false) { 495 } 496 497 ~FakeGoogle() {} 498 499 scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request) { 500 // The scheme and host of the URL is actually not important but required to 501 // get a valid GURL in order to parse |request.relative_url|. 502 GURL request_url = GURL("http://localhost").Resolve(request.relative_url); 503 LOG(WARNING) << "Requesting page " << request.relative_url; 504 std::string request_path = request_url.path(); 505 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse()); 506 if (request_path == kHelloPagePath) { // Serving "google" page. 507 start_event_.Signal(); 508 content::BrowserThread::PostTask( 509 content::BrowserThread::UI, FROM_HERE, 510 base::Bind(&FakeGoogle::QuitRunnerOnUIThread, 511 base::Unretained(this))); 512 513 http_response->set_code(net::HTTP_OK); 514 http_response->set_content_type("text/html"); 515 http_response->set_content(kGooglePageContent); 516 } else if (request_path == kRandomPagePath) { // Serving "non-google" page. 517 http_response->set_code(net::HTTP_OK); 518 http_response->set_content_type("text/html"); 519 http_response->set_content(kRandomPageContent); 520 } else { 521 return scoped_ptr<HttpResponse>(); // Request not understood. 522 } 523 524 return http_response.PassAs<HttpResponse>(); 525 } 526 527 // True if we have already served the test page. 528 bool IsPageRequested () { 529 return start_event_.IsSignaled(); 530 } 531 532 // Waits until we receive a request to serve the test page. 533 void WaitForPageRequest() { 534 // If we have already served the request, bail out. 535 if (start_event_.IsSignaled()) 536 return; 537 538 runner_ = new content::MessageLoopRunner; 539 runner_->Run(); 540 } 541 542 private: 543 void QuitRunnerOnUIThread() { 544 if (runner_.get()) 545 runner_->Quit(); 546 } 547 // This event will tell us when we actually see HTTP request on the server 548 // side. It should be signalled only after the page/XHR throttle had been 549 // removed (after merge session completes). 550 base::WaitableEvent start_event_; 551 scoped_refptr<content::MessageLoopRunner> runner_; 552 553 DISALLOW_COPY_AND_ASSIGN(FakeGoogle); 554}; 555 556// FakeGaia specialization that can delay /MergeSession handler until 557// we explicitly call DelayedFakeGaia::UnblockMergeSession(). 558class DelayedFakeGaia : public FakeGaia { 559 public: 560 DelayedFakeGaia() 561 : blocking_event_(true, false), 562 start_event_(true, false) { 563 } 564 565 void UnblockMergeSession() { 566 blocking_event_.Signal(); 567 } 568 569 void WaitForMergeSessionToStart() { 570 // If we have already served the request, bail out. 571 if (start_event_.IsSignaled()) 572 return; 573 574 runner_ = new content::MessageLoopRunner; 575 runner_->Run(); 576 } 577 578 private: 579 // FakeGaia overrides. 580 virtual void HandleMergeSession(const HttpRequest& request, 581 BasicHttpResponse* http_response) OVERRIDE { 582 start_event_.Signal(); 583 content::BrowserThread::PostTask( 584 content::BrowserThread::UI, FROM_HERE, 585 base::Bind(&DelayedFakeGaia::QuitRunnerOnUIThread, 586 base::Unretained(this))); 587 blocking_event_.Wait(); 588 FakeGaia::HandleMergeSession(request, http_response); 589 } 590 591 void QuitRunnerOnUIThread() { 592 if (runner_.get()) 593 runner_->Quit(); 594 } 595 596 base::WaitableEvent blocking_event_; 597 base::WaitableEvent start_event_; 598 scoped_refptr<content::MessageLoopRunner> runner_; 599 600 DISALLOW_COPY_AND_ASSIGN(DelayedFakeGaia); 601}; 602 603class MergeSessionTest : public OAuth2Test { 604 protected: 605 MergeSessionTest() : delayed_fake_gaia_(new DelayedFakeGaia()) { 606 fake_gaia_.reset(delayed_fake_gaia_); 607 } 608 609 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 610 OAuth2Test::SetUpCommandLine(command_line); 611 612 // Get fake URL for fake google.com. 613 const GURL& server_url = embedded_test_server()->base_url(); 614 std::string google_host("www.google.com"); 615 GURL::Replacements replace_google_host; 616 replace_google_host.SetHostStr(google_host); 617 GURL google_url = server_url.ReplaceComponents(replace_google_host); 618 fake_google_page_url_ = google_url.Resolve(kHelloPagePath); 619 620 std::string non_google_host("www.somethingelse.org"); 621 GURL::Replacements replace_non_google_host; 622 replace_non_google_host.SetHostStr(non_google_host); 623 GURL non_google_url = server_url.ReplaceComponents(replace_non_google_host); 624 non_google_page_url_ = non_google_url.Resolve(kRandomPagePath); 625} 626 627 virtual void SetUp() OVERRIDE { 628 embedded_test_server()->RegisterRequestHandler( 629 base::Bind(&FakeGoogle::HandleRequest, 630 base::Unretained(&fake_google_))); 631 OAuth2Test::SetUp(); 632 } 633 634 protected: 635 void UnblockMergeSession() { 636 delayed_fake_gaia_->UnblockMergeSession(); 637 } 638 639 void WaitForMergeSessionToStart() { 640 delayed_fake_gaia_->WaitForMergeSessionToStart(); 641 } 642 643 void JsExpect(content::WebContents* contents, 644 const std::string& expression) { 645 bool result; 646 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 647 contents, 648 "window.domAutomationController.send(!!(" + expression + "));", 649 &result)); 650 ASSERT_TRUE(result) << expression; 651 } 652 653 const GURL& GetBackGroundPageUrl(const std::string& extension_id) { 654 extensions::ProcessManager* manager = 655 extensions::ExtensionSystem::Get(profile())->process_manager(); 656 extensions::ExtensionHost* host = 657 manager->GetBackgroundHostForExtension(extension_id); 658 return host->host_contents()->GetURL(); 659 } 660 661 void JsExpectOnBackgroundPage(const std::string& extension_id, 662 const std::string& expression) { 663 extensions::ProcessManager* manager = 664 extensions::ExtensionSystem::Get(profile())->process_manager(); 665 extensions::ExtensionHost* host = 666 manager->GetBackgroundHostForExtension(extension_id); 667 if (host == NULL) { 668 ADD_FAILURE() << "Extension " << extension_id 669 << " has no background page."; 670 return; 671 } 672 673 JsExpect(host->host_contents(), expression); 674 } 675 676 FakeGoogle fake_google_; 677 DelayedFakeGaia* delayed_fake_gaia_; 678 GURL fake_google_page_url_; 679 GURL non_google_page_url_; 680 681 private: 682 DISALLOW_COPY_AND_ASSIGN(MergeSessionTest); 683}; 684 685Browser* FindOrCreateVisibleBrowser(Profile* profile) { 686 chrome::ScopedTabbedBrowserDisplayer displayer( 687 profile, chrome::GetActiveDesktop()); 688 Browser* browser = displayer.browser(); 689 if (browser->tab_strip_model()->count() == 0) 690 chrome::AddTabAt(browser, GURL(), -1, true); 691 return browser; 692} 693 694IN_PROC_BROWSER_TEST_F(MergeSessionTest, PageThrottle) { 695 StartNewUserSession(false); 696 697 // Try to open a page from google.com. 698 Browser* browser = 699 FindOrCreateVisibleBrowser(profile()); 700 ui_test_utils::NavigateToURLWithDisposition( 701 browser, 702 fake_google_page_url_, 703 CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE); 704 705 // Wait until we get send merge session request. 706 WaitForMergeSessionToStart(); 707 708 // Make sure the page is blocked by the throttle. 709 EXPECT_FALSE(fake_google_.IsPageRequested()); 710 711 // Check that throttle page is displayed instead. 712 base::string16 title; 713 ui_test_utils::GetCurrentTabTitle(browser, &title); 714 DVLOG(1) << "Loaded page at the start : " << title; 715 716 // Unblock GAIA request. 717 UnblockMergeSession(); 718 719 // Wait for the session merge to finish. 720 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_DONE); 721 722 // Make sure the test page is served. 723 fake_google_.WaitForPageRequest(); 724 725 // Check that real page is no longer blocked by the throttle and that the 726 // real page pops up JS dialog. 727 AppModalDialog* dialog = ui_test_utils::WaitForAppModalDialog(); 728 ASSERT_TRUE(dialog->IsJavaScriptModalDialog()); 729 JavaScriptAppModalDialog* js_dialog = 730 static_cast<JavaScriptAppModalDialog*>(dialog); 731 js_dialog->native_dialog()->AcceptAppModalDialog(); 732 733 ui_test_utils::GetCurrentTabTitle(browser, &title); 734 DVLOG(1) << "Loaded page at the end : " << title; 735} 736 737IN_PROC_BROWSER_TEST_F(MergeSessionTest, XHRThrottle) { 738 StartNewUserSession(false); 739 740 // Wait until we get send merge session request. 741 WaitForMergeSessionToStart(); 742 743 // Reset ExtensionBrowserTest::observer_ to the right browser object. 744 Browser* browser = FindOrCreateVisibleBrowser(profile()); 745 observer_.reset(new ExtensionTestNotificationObserver(browser)); 746 747 // Run background page tests. The tests will just wait for XHR request 748 // to complete. 749 extensions::ResultCatcher catcher; 750 751 scoped_ptr<ExtensionTestMessageListener> non_google_xhr_listener( 752 new ExtensionTestMessageListener("non-google-xhr-received", false)); 753 754 // Load extension with a background page. The background page will 755 // attempt to load |fake_google_page_url_| via XHR. 756 const extensions::Extension* ext = LoadExtension( 757 test_data_dir_.AppendASCII("merge_session")); 758 ASSERT_TRUE(ext); 759 760 // Kick off XHR request from the extension. 761 JsExpectOnBackgroundPage( 762 ext->id(), 763 base::StringPrintf("startThrottledTests('%s', '%s')", 764 fake_google_page_url_.spec().c_str(), 765 non_google_page_url_.spec().c_str())); 766 767 // Verify that we've sent XHR request form the extension side... 768 JsExpectOnBackgroundPage(ext->id(), 769 "googleRequestSent && !googleResponseReceived"); 770 771 // ...but didn't see it on the server side yet. 772 EXPECT_FALSE(fake_google_.IsPageRequested()); 773 774 // Unblock GAIA request. 775 UnblockMergeSession(); 776 777 // Wait for the session merge to finish. 778 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_DONE); 779 780 // Wait until non-google XHR content to load first. 781 ASSERT_TRUE(non_google_xhr_listener->WaitUntilSatisfied()); 782 783 if (!catcher.GetNextResult()) { 784 std::string message = catcher.message(); 785 ADD_FAILURE() << "Tests failed: " << message; 786 } 787 788 EXPECT_TRUE(fake_google_.IsPageRequested()); 789} 790 791} // namespace chromeos 792