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