OLD | NEW |
| (Empty) |
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 | |
OLD | NEW |