Home | History | Annotate | Download | only in signin
      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