Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / chromeos / login / signin / oauth2_browsertest.cc
blobd2e8786001df0bf19aebe851c6f6676e09e4881c
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.
5 #include <string>
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;
57 namespace chromeos {
59 namespace {
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(),
85 gaia_id, email);
88 class OAuth2LoginManagerStateWaiter : public OAuth2LoginManager::Observer {
89 public:
90 explicit OAuth2LoginManagerStateWaiter(Profile* profile)
91 : profile_(profile),
92 waiting_for_state_(false),
93 final_state_(OAuth2LoginManager::SESSION_RESTORE_NOT_STARTED) {
96 void WaitForStates(
97 const std::set<OAuth2LoginManager::SessionRestoreState>& states) {
98 DCHECK(!waiting_for_state_);
99 OAuth2LoginManager* login_manager =
100 OAuth2LoginManagerFactory::GetInstance()->GetForProfile(profile_);
101 states_ = states;
102 if (states_.find(login_manager->state()) != states_.end()) {
103 final_state_ = login_manager->state();
104 return;
107 waiting_for_state_ = true;
108 login_manager->AddObserver(this);
109 runner_ = new content::MessageLoopRunner;
110 runner_->Run();
111 login_manager->RemoveObserver(this);
114 OAuth2LoginManager::SessionRestoreState final_state() { return final_state_; }
116 private:
117 // OAuth2LoginManager::Observer overrides.
118 void OnSessionRestoreStateChanged(
119 Profile* user_profile,
120 OAuth2LoginManager::SessionRestoreState state) override {
121 if (!waiting_for_state_)
122 return;
124 if (states_.find(state) == states_.end())
125 return;
127 final_state_ = state;
128 waiting_for_state_ = false;
129 runner_->Quit();
132 Profile* profile_;
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);
141 } // namespace
143 // Boolean parameter is used to run this test for webview (true) and for
144 // iframe (false) GAIA sign in.
145 class OAuth2Test : public OobeBaseTest,
146 public testing::WithParamInterface<bool> {
147 protected:
148 OAuth2Test() { set_use_webview(GetParam()); }
150 void SetUpCommandLine(base::CommandLine* command_line) override {
151 OobeBaseTest::SetUpCommandLine(command_line);
153 // Disable sync sinc we don't really need this for these tests and it also
154 // makes OAuth2Test.MergeSession test flaky http://crbug.com/408867.
155 command_line->AppendSwitch(switches::kDisableSync);
158 void SetupGaiaServerForNewAccount() {
159 FakeGaia::MergeSessionParams params;
160 params.auth_sid_cookie = kTestAuthSIDCookie;
161 params.auth_lsid_cookie = kTestAuthLSIDCookie;
162 params.auth_code = kTestAuthCode;
163 params.refresh_token = kTestRefreshToken;
164 params.access_token = kTestAuthLoginAccessToken;
165 params.gaia_uber_token = kTestGaiaUberToken;
166 params.session_sid_cookie = kTestSessionSIDCookie;
167 params.session_lsid_cookie = kTestSessionLSIDCookie;
168 fake_gaia_->SetMergeSessionParams(params);
169 SetupGaiaServerWithAccessTokens();
172 void SetupGaiaServerForUnexpiredAccount() {
173 FakeGaia::MergeSessionParams params;
174 params.email = kTestEmail;
175 fake_gaia_->SetMergeSessionParams(params);
176 SetupGaiaServerWithAccessTokens();
179 void SetupGaiaServerForExpiredAccount() {
180 FakeGaia::MergeSessionParams params;
181 params.gaia_uber_token = kTestGaiaUberToken;
182 params.session_sid_cookie = kTestSession2SIDCookie;
183 params.session_lsid_cookie = kTestSession2LSIDCookie;
184 fake_gaia_->SetMergeSessionParams(params);
185 SetupGaiaServerWithAccessTokens();
188 void LoginAsExistingUser() {
189 content::WindowedNotificationObserver(
190 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
191 content::NotificationService::AllSources()).Wait();
193 JsExpect("!!document.querySelector('#account-picker')");
194 JsExpect("!!document.querySelector('#pod-row')");
196 std::string account_id = PickAccountId(
197 ProfileManager::GetPrimaryUserProfile(), kTestGaiaId, kTestEmail);
199 EXPECT_EQ(GetOAuthStatusFromLocalState(account_id),
200 user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
202 // Try login. Primary profile has changed.
203 EXPECT_TRUE(TryToLogin(kTestGaiaId, kTestEmail, kTestAccountPassword));
204 Profile* profile = ProfileManager::GetPrimaryUserProfile();
206 // Wait for the session merge to finish.
207 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_DONE);
209 // Check for existance of refresh token.
210 ProfileOAuth2TokenService* token_service =
211 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
212 EXPECT_TRUE(token_service->RefreshTokenIsAvailable(account_id));
214 EXPECT_EQ(GetOAuthStatusFromLocalState(account_id),
215 user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
218 bool TryToLogin(const std::string& gaia_id,
219 const std::string& username,
220 const std::string& password) {
221 if (!AddUserToSession(gaia_id, username, password))
222 return false;
224 if (const user_manager::User* active_user =
225 user_manager::UserManager::Get()->GetActiveUser()) {
226 return active_user->email() == username;
229 return false;
232 user_manager::User::OAuthTokenStatus GetOAuthStatusFromLocalState(
233 const std::string& account_id) const {
234 PrefService* local_state = g_browser_process->local_state();
235 const base::DictionaryValue* prefs_oauth_status =
236 local_state->GetDictionary("OAuthTokenStatus");
237 int oauth_token_status = user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN;
238 if (prefs_oauth_status &&
239 prefs_oauth_status->GetIntegerWithoutPathExpansion(
240 account_id, &oauth_token_status)) {
241 user_manager::User::OAuthTokenStatus result =
242 static_cast<user_manager::User::OAuthTokenStatus>(oauth_token_status);
243 return result;
245 return user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN;
248 protected:
249 // OobeBaseTest overrides.
250 Profile* profile() override {
251 if (user_manager::UserManager::Get()->GetActiveUser())
252 return ProfileManager::GetPrimaryUserProfile();
254 return OobeBaseTest::profile();
257 bool AddUserToSession(const std::string& gaia_id,
258 const std::string& username,
259 const std::string& password) {
260 ExistingUserController* controller =
261 ExistingUserController::current_controller();
262 if (!controller) {
263 ADD_FAILURE();
264 return false;
267 UserContext user_context(username);
268 user_context.SetGaiaID(gaia_id);
269 user_context.SetKey(Key(password));
270 controller->Login(user_context, SigninSpecifics());
271 content::WindowedNotificationObserver(
272 chrome::NOTIFICATION_SESSION_STARTED,
273 content::NotificationService::AllSources()).Wait();
274 const user_manager::UserList& logged_users =
275 user_manager::UserManager::Get()->GetLoggedInUsers();
276 for (user_manager::UserList::const_iterator it = logged_users.begin();
277 it != logged_users.end();
278 ++it) {
279 if ((*it)->email() == username)
280 return true;
282 return false;
285 void SetupGaiaServerWithAccessTokens() {
286 fake_gaia_->MapEmailToGaiaId(kTestEmail, kTestGaiaId);
288 // Configure OAuth authentication.
289 GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
291 // This token satisfies the userinfo.email request from
292 // DeviceOAuth2TokenService used in token validation.
293 FakeGaia::AccessTokenInfo userinfo_token_info;
294 userinfo_token_info.token = kTestUserinfoToken;
295 userinfo_token_info.scopes.insert(
296 "https://www.googleapis.com/auth/userinfo.email");
297 userinfo_token_info.audience = gaia_urls->oauth2_chrome_client_id();
298 userinfo_token_info.email = kTestEmail;
299 fake_gaia_->IssueOAuthToken(kTestRefreshToken, userinfo_token_info);
301 FakeGaia::AccessTokenInfo userinfo_profile_token_info;
302 userinfo_profile_token_info.token = kTestUserinfoToken;
303 userinfo_profile_token_info.scopes.insert(
304 "https://www.googleapis.com/auth/userinfo.profile");
305 userinfo_profile_token_info.audience = gaia_urls->oauth2_chrome_client_id();
306 userinfo_profile_token_info.email = kTestEmail;
307 fake_gaia_->IssueOAuthToken(kTestRefreshToken, userinfo_profile_token_info);
309 // The any-api access token for accessing the token minting endpoint.
310 FakeGaia::AccessTokenInfo login_token_info;
311 login_token_info.token = kTestLoginToken;
312 login_token_info.scopes.insert(GaiaConstants::kAnyApiOAuth2Scope);
313 login_token_info.audience = gaia_urls->oauth2_chrome_client_id();
314 fake_gaia_->IssueOAuthToken(kTestRefreshToken, login_token_info);
316 // The /auth/chromesync access token for accessing sync endpoint.
317 FakeGaia::AccessTokenInfo sync_token_info;
318 sync_token_info.token = kTestSyncToken;
319 sync_token_info.scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope);
320 sync_token_info.audience = gaia_urls->oauth2_chrome_client_id();
321 fake_gaia_->IssueOAuthToken(kTestRefreshToken, sync_token_info);
323 FakeGaia::AccessTokenInfo auth_login_token_info;
324 auth_login_token_info.token = kTestAuthLoginToken;
325 auth_login_token_info.scopes.insert(GaiaConstants::kOAuth1LoginScope);
326 auth_login_token_info.audience = gaia_urls->oauth2_chrome_client_id();
327 fake_gaia_->IssueOAuthToken(kTestRefreshToken, auth_login_token_info);
330 void CheckSessionState(OAuth2LoginManager::SessionRestoreState state) {
331 OAuth2LoginManager* login_manager =
332 OAuth2LoginManagerFactory::GetInstance()->GetForProfile(
333 profile());
334 ASSERT_EQ(state, login_manager->state());
337 void WaitForMergeSessionCompletion(
338 OAuth2LoginManager::SessionRestoreState final_state) {
339 // Wait for the session merge to finish.
340 std::set<OAuth2LoginManager::SessionRestoreState> states;
341 states.insert(OAuth2LoginManager::SESSION_RESTORE_DONE);
342 states.insert(OAuth2LoginManager::SESSION_RESTORE_FAILED);
343 states.insert(OAuth2LoginManager::SESSION_RESTORE_CONNECTION_FAILED);
344 OAuth2LoginManagerStateWaiter merge_session_waiter(profile());
345 merge_session_waiter.WaitForStates(states);
346 EXPECT_EQ(merge_session_waiter.final_state(), final_state);
349 void StartNewUserSession(bool wait_for_merge) {
350 SetupGaiaServerForNewAccount();
351 SimulateNetworkOnline();
352 WaitForGaiaPageLoad();
354 content::WindowedNotificationObserver session_start_waiter(
355 chrome::NOTIFICATION_SESSION_STARTED,
356 content::NotificationService::AllSources());
358 // Use capitalized and dotted user name on purpose to make sure
359 // our email normalization kicks in.
360 GetLoginDisplay()->ShowSigninScreenForCreds(kTestRawEmail,
361 kTestAccountPassword);
362 session_start_waiter.Wait();
364 if (wait_for_merge) {
365 // Wait for the session merge to finish.
366 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_DONE);
370 DISALLOW_COPY_AND_ASSIGN(OAuth2Test);
373 class CookieReader : public base::RefCountedThreadSafe<CookieReader> {
374 public:
375 CookieReader() {
378 void ReadCookies(Profile* profile) {
379 context_ = profile->GetRequestContext();
380 content::BrowserThread::PostTask(
381 content::BrowserThread::IO, FROM_HERE,
382 base::Bind(&CookieReader::ReadCookiesOnIOThread,
383 this));
384 runner_ = new content::MessageLoopRunner;
385 runner_->Run();
388 std::string GetCookieValue(const std::string& name) {
389 for (std::vector<net::CanonicalCookie>::const_iterator iter =
390 cookie_list_.begin();
391 iter != cookie_list_.end();
392 ++iter) {
393 if (iter->Name() == name) {
394 return iter->Value();
397 return std::string();
400 private:
401 friend class base::RefCountedThreadSafe<CookieReader>;
403 virtual ~CookieReader() {
406 void ReadCookiesOnIOThread() {
407 context_->GetURLRequestContext()->cookie_store()->GetCookieMonster()->
408 GetAllCookiesAsync(base::Bind(
409 &CookieReader::OnGetAllCookiesOnUIThread,
410 this));
413 void OnGetAllCookiesOnUIThread(const net::CookieList& cookies) {
414 cookie_list_ = cookies;
415 content::BrowserThread::PostTask(
416 content::BrowserThread::UI, FROM_HERE,
417 base::Bind(&CookieReader::OnCookiesReadyOnUIThread,
418 this));
421 void OnCookiesReadyOnUIThread() {
422 runner_->Quit();
425 scoped_refptr<net::URLRequestContextGetter> context_;
426 net::CookieList cookie_list_;
427 scoped_refptr<content::MessageLoopRunner> runner_;
429 DISALLOW_COPY_AND_ASSIGN(CookieReader);
432 // PRE_MergeSession is testing merge session for a new profile.
433 IN_PROC_BROWSER_TEST_P(OAuth2Test, PRE_PRE_PRE_MergeSession) {
434 StartNewUserSession(true);
435 // Check for existance of refresh token.
436 std::string account_id = PickAccountId(profile(), kTestGaiaId, kTestEmail);
437 ProfileOAuth2TokenService* token_service =
438 ProfileOAuth2TokenServiceFactory::GetForProfile(
439 profile());
440 EXPECT_TRUE(token_service->RefreshTokenIsAvailable(account_id));
442 EXPECT_EQ(GetOAuthStatusFromLocalState(account_id),
443 user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
444 scoped_refptr<CookieReader> cookie_reader(new CookieReader());
445 cookie_reader->ReadCookies(profile());
446 EXPECT_EQ(cookie_reader->GetCookieValue("SID"), kTestSessionSIDCookie);
447 EXPECT_EQ(cookie_reader->GetCookieValue("LSID"), kTestSessionLSIDCookie);
450 // MergeSession test is running merge session process for an existing profile
451 // that was generated in PRE_PRE_PRE_MergeSession test. In this test, we
452 // are not running /MergeSession process since the /ListAccounts call confirms
453 // that the session is not stale.
454 IN_PROC_BROWSER_TEST_P(OAuth2Test, PRE_PRE_MergeSession) {
455 SetupGaiaServerForUnexpiredAccount();
456 SimulateNetworkOnline();
457 LoginAsExistingUser();
458 scoped_refptr<CookieReader> cookie_reader(new CookieReader());
459 cookie_reader->ReadCookies(profile());
460 // These are still cookie values form the initial session since
461 // /ListAccounts
462 EXPECT_EQ(cookie_reader->GetCookieValue("SID"), kTestSessionSIDCookie);
463 EXPECT_EQ(cookie_reader->GetCookieValue("LSID"), kTestSessionLSIDCookie);
466 // MergeSession test is running merge session process for an existing profile
467 // that was generated in PRE_PRE_MergeSession test.
468 // Disabled due to flakiness: crbug.com/496832
469 IN_PROC_BROWSER_TEST_P(OAuth2Test, DISABLED_PRE_MergeSession) {
470 SetupGaiaServerForExpiredAccount();
471 SimulateNetworkOnline();
472 LoginAsExistingUser();
473 scoped_refptr<CookieReader> cookie_reader(new CookieReader());
474 cookie_reader->ReadCookies(profile());
475 // These should be cookie values that we generated by calling /MergeSession,
476 // since /ListAccounts should have tell us that the initial session cookies
477 // are stale.
478 EXPECT_EQ(cookie_reader->GetCookieValue("SID"), kTestSession2SIDCookie);
479 EXPECT_EQ(cookie_reader->GetCookieValue("LSID"), kTestSession2LSIDCookie);
482 // MergeSession test is attempting to merge session for an existing profile
483 // that was generated in PRE_PRE_MergeSession test. This attempt should fail
484 // since FakeGaia instance isn't configured to return relevant tokens/cookies.
485 // Disabled due to flakiness: crbug.com/496832
486 IN_PROC_BROWSER_TEST_P(OAuth2Test, DISABLED_MergeSession) {
487 SimulateNetworkOnline();
489 content::WindowedNotificationObserver(
490 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
491 content::NotificationService::AllSources()).Wait();
493 JsExpect("!!document.querySelector('#account-picker')");
494 JsExpect("!!document.querySelector('#pod-row')");
496 std::string account_id = PickAccountId(profile(), kTestGaiaId, kTestEmail);
497 EXPECT_EQ(GetOAuthStatusFromLocalState(account_id),
498 user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
500 EXPECT_TRUE(TryToLogin(kTestGaiaId, kTestEmail, kTestAccountPassword));
502 // Wait for the session merge to finish.
503 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_FAILED);
505 EXPECT_EQ(GetOAuthStatusFromLocalState(account_id),
506 user_manager::User::OAUTH2_TOKEN_STATUS_INVALID);
510 const char kGooglePageContent[] =
511 "<html><title>Hello!</title><script>alert('hello');</script>"
512 "<body>Hello Google!</body></html>";
513 const char kRandomPageContent[] =
514 "<html><title>SomthingElse</title><body>I am SomethingElse</body></html>";
515 const char kHelloPagePath[] = "/hello_google";
516 const char kRandomPagePath[] = "/non_google_page";
519 // FakeGoogle serves content of http://www.google.com/hello_google page for
520 // merge session tests.
521 class FakeGoogle {
522 public:
523 FakeGoogle() : start_event_(true, false) {
526 ~FakeGoogle() {}
528 scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request) {
529 // The scheme and host of the URL is actually not important but required to
530 // get a valid GURL in order to parse |request.relative_url|.
531 GURL request_url = GURL("http://localhost").Resolve(request.relative_url);
532 LOG(WARNING) << "Requesting page " << request.relative_url;
533 std::string request_path = request_url.path();
534 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
535 if (request_path == kHelloPagePath) { // Serving "google" page.
536 start_event_.Signal();
537 content::BrowserThread::PostTask(
538 content::BrowserThread::UI, FROM_HERE,
539 base::Bind(&FakeGoogle::QuitRunnerOnUIThread,
540 base::Unretained(this)));
542 http_response->set_code(net::HTTP_OK);
543 http_response->set_content_type("text/html");
544 http_response->set_content(kGooglePageContent);
545 } else if (request_path == kRandomPagePath) { // Serving "non-google" page.
546 http_response->set_code(net::HTTP_OK);
547 http_response->set_content_type("text/html");
548 http_response->set_content(kRandomPageContent);
549 } else {
550 return scoped_ptr<HttpResponse>(); // Request not understood.
553 return http_response.Pass();
556 // True if we have already served the test page.
557 bool IsPageRequested() { return start_event_.IsSignaled(); }
559 // Waits until we receive a request to serve the test page.
560 void WaitForPageRequest() {
561 // If we have already served the request, bail out.
562 if (start_event_.IsSignaled())
563 return;
565 runner_ = new content::MessageLoopRunner;
566 runner_->Run();
569 private:
570 void QuitRunnerOnUIThread() {
571 if (runner_.get())
572 runner_->Quit();
574 // This event will tell us when we actually see HTTP request on the server
575 // side. It should be signalled only after the page/XHR throttle had been
576 // removed (after merge session completes).
577 base::WaitableEvent start_event_;
578 scoped_refptr<content::MessageLoopRunner> runner_;
580 DISALLOW_COPY_AND_ASSIGN(FakeGoogle);
583 // FakeGaia specialization that can delay /MergeSession handler until
584 // we explicitly call DelayedFakeGaia::UnblockMergeSession().
585 class DelayedFakeGaia : public FakeGaia {
586 public:
587 DelayedFakeGaia()
588 : blocking_event_(true, false),
589 start_event_(true, false) {
592 void UnblockMergeSession() {
593 blocking_event_.Signal();
596 void WaitForMergeSessionToStart() {
597 // If we have already served the request, bail out.
598 if (start_event_.IsSignaled())
599 return;
601 runner_ = new content::MessageLoopRunner;
602 runner_->Run();
605 private:
606 // FakeGaia overrides.
607 void HandleMergeSession(const HttpRequest& request,
608 BasicHttpResponse* http_response) override {
609 start_event_.Signal();
610 content::BrowserThread::PostTask(
611 content::BrowserThread::UI, FROM_HERE,
612 base::Bind(&DelayedFakeGaia::QuitRunnerOnUIThread,
613 base::Unretained(this)));
614 blocking_event_.Wait();
615 FakeGaia::HandleMergeSession(request, http_response);
618 void QuitRunnerOnUIThread() {
619 if (runner_.get())
620 runner_->Quit();
623 base::WaitableEvent blocking_event_;
624 base::WaitableEvent start_event_;
625 scoped_refptr<content::MessageLoopRunner> runner_;
627 DISALLOW_COPY_AND_ASSIGN(DelayedFakeGaia);
630 class MergeSessionTest : public OAuth2Test {
631 protected:
632 MergeSessionTest() : delayed_fake_gaia_(new DelayedFakeGaia()) {
633 fake_gaia_.reset(delayed_fake_gaia_);
636 void SetUpCommandLine(base::CommandLine* command_line) override {
637 OAuth2Test::SetUpCommandLine(command_line);
639 // Get fake URL for fake google.com.
640 const GURL& server_url = embedded_test_server()->base_url();
641 GURL::Replacements replace_google_host;
642 replace_google_host.SetHostStr("www.google.com");
643 GURL google_url = server_url.ReplaceComponents(replace_google_host);
644 fake_google_page_url_ = google_url.Resolve(kHelloPagePath);
646 GURL::Replacements replace_non_google_host;
647 replace_non_google_host.SetHostStr("www.somethingelse.org");
648 GURL non_google_url = server_url.ReplaceComponents(replace_non_google_host);
649 non_google_page_url_ = non_google_url.Resolve(kRandomPagePath);
652 void SetUp() override {
653 embedded_test_server()->RegisterRequestHandler(
654 base::Bind(&FakeGoogle::HandleRequest,
655 base::Unretained(&fake_google_)));
656 OAuth2Test::SetUp();
659 protected:
660 void UnblockMergeSession() {
661 delayed_fake_gaia_->UnblockMergeSession();
664 void WaitForMergeSessionToStart() {
665 delayed_fake_gaia_->WaitForMergeSessionToStart();
668 void JsExpect(content::WebContents* contents,
669 const std::string& expression) {
670 bool result;
671 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
672 contents,
673 "window.domAutomationController.send(!!(" + expression + "));",
674 &result));
675 ASSERT_TRUE(result) << expression;
678 const GURL& GetBackGroundPageUrl(const std::string& extension_id) {
679 extensions::ProcessManager* manager =
680 extensions::ProcessManager::Get(profile());
681 extensions::ExtensionHost* host =
682 manager->GetBackgroundHostForExtension(extension_id);
683 return host->host_contents()->GetURL();
686 void JsExpectOnBackgroundPage(const std::string& extension_id,
687 const std::string& expression) {
688 extensions::ProcessManager* manager =
689 extensions::ProcessManager::Get(profile());
690 extensions::ExtensionHost* host =
691 manager->GetBackgroundHostForExtension(extension_id);
692 if (host == NULL) {
693 ADD_FAILURE() << "Extension " << extension_id
694 << " has no background page.";
695 return;
698 JsExpect(host->host_contents(), expression);
701 FakeGoogle fake_google_;
702 DelayedFakeGaia* delayed_fake_gaia_;
703 GURL fake_google_page_url_;
704 GURL non_google_page_url_;
706 private:
707 DISALLOW_COPY_AND_ASSIGN(MergeSessionTest);
710 Browser* FindOrCreateVisibleBrowser(Profile* profile) {
711 chrome::ScopedTabbedBrowserDisplayer displayer(
712 profile, chrome::GetActiveDesktop());
713 Browser* browser = displayer.browser();
714 if (browser->tab_strip_model()->count() == 0)
715 chrome::AddTabAt(browser, GURL(), -1, true);
716 return browser;
719 IN_PROC_BROWSER_TEST_P(MergeSessionTest, PageThrottle) {
720 StartNewUserSession(false);
722 // Try to open a page from google.com.
723 Browser* browser =
724 FindOrCreateVisibleBrowser(profile());
725 ui_test_utils::NavigateToURLWithDisposition(
726 browser,
727 fake_google_page_url_,
728 CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE);
730 // Wait until we get send merge session request.
731 WaitForMergeSessionToStart();
733 // Make sure the page is blocked by the throttle.
734 EXPECT_FALSE(fake_google_.IsPageRequested());
736 // Check that throttle page is displayed instead.
737 base::string16 title;
738 ui_test_utils::GetCurrentTabTitle(browser, &title);
739 DVLOG(1) << "Loaded page at the start : " << title;
741 // Unblock GAIA request.
742 UnblockMergeSession();
744 // Wait for the session merge to finish.
745 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_DONE);
747 // Make sure the test page is served.
748 fake_google_.WaitForPageRequest();
750 // Check that real page is no longer blocked by the throttle and that the
751 // real page pops up JS dialog.
752 AppModalDialog* dialog = ui_test_utils::WaitForAppModalDialog();
753 ASSERT_TRUE(dialog->IsJavaScriptModalDialog());
754 JavaScriptAppModalDialog* js_dialog =
755 static_cast<JavaScriptAppModalDialog*>(dialog);
756 js_dialog->native_dialog()->AcceptAppModalDialog();
758 ui_test_utils::GetCurrentTabTitle(browser, &title);
759 DVLOG(1) << "Loaded page at the end : " << title;
762 IN_PROC_BROWSER_TEST_P(MergeSessionTest, XHRThrottle) {
763 StartNewUserSession(false);
765 // Wait until we get send merge session request.
766 WaitForMergeSessionToStart();
768 // Reset ExtensionBrowserTest::observer_ to the right browser object.
769 Browser* browser = FindOrCreateVisibleBrowser(profile());
770 observer_.reset(new ExtensionTestNotificationObserver(browser));
772 // Run background page tests. The tests will just wait for XHR request
773 // to complete.
774 extensions::ResultCatcher catcher;
776 scoped_ptr<ExtensionTestMessageListener> non_google_xhr_listener(
777 new ExtensionTestMessageListener("non-google-xhr-received", false));
779 // Load extension with a background page. The background page will
780 // attempt to load |fake_google_page_url_| via XHR.
781 const extensions::Extension* ext = LoadExtension(
782 test_data_dir_.AppendASCII("merge_session"));
783 ASSERT_TRUE(ext);
785 // Kick off XHR request from the extension.
786 JsExpectOnBackgroundPage(
787 ext->id(),
788 base::StringPrintf("startThrottledTests('%s', '%s')",
789 fake_google_page_url_.spec().c_str(),
790 non_google_page_url_.spec().c_str()));
792 // Verify that we've sent XHR request form the extension side...
793 JsExpectOnBackgroundPage(ext->id(),
794 "googleRequestSent && !googleResponseReceived");
796 // ...but didn't see it on the server side yet.
797 EXPECT_FALSE(fake_google_.IsPageRequested());
799 // Unblock GAIA request.
800 UnblockMergeSession();
802 // Wait for the session merge to finish.
803 WaitForMergeSessionCompletion(OAuth2LoginManager::SESSION_RESTORE_DONE);
805 // Wait until non-google XHR content to load first.
806 ASSERT_TRUE(non_google_xhr_listener->WaitUntilSatisfied());
808 if (!catcher.GetNextResult()) {
809 std::string message = catcher.message();
810 ADD_FAILURE() << "Tests failed: " << message;
813 EXPECT_TRUE(fake_google_.IsPageRequested());
816 INSTANTIATE_TEST_CASE_P(OAuth2Suite, OAuth2Test, testing::Bool());
817 INSTANTIATE_TEST_CASE_P(MergeSessionSuite, MergeSessionTest, testing::Bool());
819 } // namespace chromeos