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