Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(246)

Side by Side Diff: chrome/browser/signin/account_reconcilor.cc

Issue 219933002: Componentize AccountReconcilor. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 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 "chrome/browser/signin/account_reconcilor.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/json/json_reader.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/message_loop/message_loop_proxy.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/time/time.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
18 #include "chrome/browser/signin/signin_manager_factory.h"
19 #include "chrome/browser/signin/signin_oauth_helper.h"
20 #include "components/signin/core/browser/profile_oauth2_token_service.h"
21 #include "components/signin/core/browser/signin_client.h"
22 #include "google_apis/gaia/gaia_auth_fetcher.h"
23 #include "google_apis/gaia/gaia_auth_util.h"
24 #include "google_apis/gaia/gaia_constants.h"
25 #include "google_apis/gaia/gaia_oauth_client.h"
26 #include "google_apis/gaia/gaia_urls.h"
27
28 // Fetches a refresh token from the given session in the GAIA cookie. This is
29 // a best effort only. If it should fail, another reconcile action will occur
30 // shortly anyway.
31 class AccountReconcilor::RefreshTokenFetcher
32 : public SigninOAuthHelper,
33 public SigninOAuthHelper::Consumer {
34 public:
35 RefreshTokenFetcher(AccountReconcilor* reconcilor,
36 const std::string& account_id,
37 int session_index);
38 virtual ~RefreshTokenFetcher() {}
39
40 private:
41 // Overridden from GaiaAuthConsumer:
42 virtual void OnSigninOAuthInformationAvailable(
43 const std::string& email,
44 const std::string& display_email,
45 const std::string& refresh_token) OVERRIDE;
46
47 // Called when an error occurs while getting the information.
48 virtual void OnSigninOAuthInformationFailure(
49 const GoogleServiceAuthError& error) OVERRIDE;
50
51 AccountReconcilor* reconcilor_;
52 const std::string account_id_;
53 int session_index_;
54
55 DISALLOW_COPY_AND_ASSIGN(RefreshTokenFetcher);
56 };
57
58 AccountReconcilor::RefreshTokenFetcher::RefreshTokenFetcher(
59 AccountReconcilor* reconcilor,
60 const std::string& account_id,
61 int session_index)
62 : SigninOAuthHelper(reconcilor->profile()->GetRequestContext(),
63 base::IntToString(session_index), this),
64 reconcilor_(reconcilor),
65 account_id_(account_id),
66 session_index_(session_index) {
67 DCHECK(reconcilor_);
68 DCHECK(!account_id.empty());
69 }
70
71 void AccountReconcilor::RefreshTokenFetcher::OnSigninOAuthInformationAvailable(
72 const std::string& email,
73 const std::string& display_email,
74 const std::string& refresh_token) {
75 VLOG(1) << "RefreshTokenFetcher::OnSigninOAuthInformationAvailable:"
76 << " account=" << account_id_
77 << " email=" << email
78 << " displayEmail=" << display_email;
79
80 // TODO(rogerta): because of the problem with email vs displayEmail and
81 // emails that have been canonicalized, the argument |email| is used here
82 // to make sure the correct string is used when calling the token service.
83 // This will be cleaned up when chrome moves to using gaia obfuscated id.
84 reconcilor_->HandleRefreshTokenFetched(email, refresh_token);
85 }
86
87 void AccountReconcilor::RefreshTokenFetcher::OnSigninOAuthInformationFailure(
88 const GoogleServiceAuthError& error) {
89 VLOG(1) << "RefreshTokenFetcher::OnSigninOAuthInformationFailure:"
90 << " account=" << account_id_
91 << " session_index=" << session_index_;
92 reconcilor_->HandleRefreshTokenFetched(account_id_, std::string());
93 }
94
95
96 bool AccountReconcilor::EmailLessFunc::operator()(const std::string& s1,
97 const std::string& s2) const {
98 return gaia::CanonicalizeEmail(s1) < gaia::CanonicalizeEmail(s2);
99 }
100
101 class AccountReconcilor::UserIdFetcher
102 : public gaia::GaiaOAuthClient::Delegate {
103 public:
104 UserIdFetcher(AccountReconcilor* reconcilor,
105 const std::string& access_token,
106 const std::string& account_id);
107
108 // Returns the scopes needed by the UserIdFetcher.
109 static OAuth2TokenService::ScopeSet GetScopes();
110
111 private:
112 // Overriden from gaia::GaiaOAuthClient::Delegate.
113 virtual void OnGetUserIdResponse(const std::string& user_id) OVERRIDE;
114 virtual void OnOAuthError() OVERRIDE;
115 virtual void OnNetworkError(int response_code) OVERRIDE;
116
117 AccountReconcilor* const reconcilor_;
118 const std::string account_id_;
119 const std::string access_token_;
120 gaia::GaiaOAuthClient gaia_auth_client_;
121
122 DISALLOW_COPY_AND_ASSIGN(UserIdFetcher);
123 };
124
125 AccountReconcilor::UserIdFetcher::UserIdFetcher(AccountReconcilor* reconcilor,
126 const std::string& access_token,
127 const std::string& account_id)
128 : reconcilor_(reconcilor),
129 account_id_(account_id),
130 access_token_(access_token),
131 gaia_auth_client_(reconcilor_->profile()->GetRequestContext()) {
132 DCHECK(reconcilor_);
133 DCHECK(!account_id_.empty());
134
135 const int kMaxRetries = 5;
136 gaia_auth_client_.GetUserId(access_token_, kMaxRetries, this);
137 }
138
139 // static
140 OAuth2TokenService::ScopeSet AccountReconcilor::UserIdFetcher::GetScopes() {
141 OAuth2TokenService::ScopeSet scopes;
142 scopes.insert("https://www.googleapis.com/auth/userinfo.profile");
143 return scopes;
144 }
145
146 void AccountReconcilor::UserIdFetcher::OnGetUserIdResponse(
147 const std::string& user_id) {
148 VLOG(1) << "AccountReconcilor::OnGetUserIdResponse: " << account_id_;
149
150 // HandleSuccessfulAccountIdCheck() may delete |this|, so call it last.
151 reconcilor_->HandleSuccessfulAccountIdCheck(account_id_);
152 }
153
154 void AccountReconcilor::UserIdFetcher::OnOAuthError() {
155 VLOG(1) << "AccountReconcilor::OnOAuthError: " << account_id_;
156
157 // Invalidate the access token to force a refetch next time.
158 ProfileOAuth2TokenService* token_service =
159 ProfileOAuth2TokenServiceFactory::GetForProfile(reconcilor_->profile());
160 token_service->InvalidateToken(account_id_, GetScopes(), access_token_);
161
162 // HandleFailedAccountIdCheck() may delete |this|, so call it last.
163 reconcilor_->HandleFailedAccountIdCheck(account_id_);
164 }
165
166 void AccountReconcilor::UserIdFetcher::OnNetworkError(int response_code) {
167 VLOG(1) << "AccountReconcilor::OnNetworkError: " << account_id_
168 << " response_code=" << response_code;
169
170 // TODO(rogerta): some response error should not be treated like
171 // permanent errors. Figure out appropriate ones.
172 // HandleFailedAccountIdCheck() may delete |this|, so call it last.
173 reconcilor_->HandleFailedAccountIdCheck(account_id_);
174 }
175
176 AccountReconcilor::AccountReconcilor(Profile* profile, SigninClient* client)
177 : OAuth2TokenService::Consumer("account_reconcilor"),
178 profile_(profile),
179 client_(client),
180 merge_session_helper_(
181 ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
182 profile->GetRequestContext(),
183 this),
184 registered_with_token_service_(false),
185 is_reconcile_started_(false),
186 are_gaia_accounts_set_(false),
187 requests_(NULL) {
188 VLOG(1) << "AccountReconcilor::AccountReconcilor";
189 }
190
191 AccountReconcilor::~AccountReconcilor() {
192 VLOG(1) << "AccountReconcilor::~AccountReconcilor";
193 // Make sure shutdown was called first.
194 DCHECK(!registered_with_token_service_);
195 DCHECK(!reconciliation_timer_.IsRunning());
196 DCHECK(!requests_);
197 DCHECK_EQ(0u, user_id_fetchers_.size());
198 DCHECK_EQ(0u, refresh_token_fetchers_.size());
199 }
200
201 void AccountReconcilor::Initialize(bool start_reconcile_if_tokens_available) {
202 VLOG(1) << "AccountReconcilor::Initialize";
203 RegisterWithSigninManager();
204
205 // If this profile is not connected, the reconcilor should do nothing but
206 // wait for the connection.
207 if (IsProfileConnected()) {
208 RegisterForCookieChanges();
209 RegisterWithTokenService();
210 StartPeriodicReconciliation();
211
212 // Start a reconcile if the tokens are already loaded.
213 ProfileOAuth2TokenService* token_service =
214 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
215 if (start_reconcile_if_tokens_available &&
216 token_service->GetAccounts().size() > 0) {
217 StartReconcile();
218 }
219 }
220 }
221
222 void AccountReconcilor::Shutdown() {
223 VLOG(1) << "AccountReconcilor::Shutdown";
224 merge_session_helper_.CancelAll();
225 merge_session_helper_.RemoveObserver(this);
226 gaia_fetcher_.reset();
227 DeleteFetchers();
228 UnregisterWithSigninManager();
229 UnregisterWithTokenService();
230 UnregisterForCookieChanges();
231 StopPeriodicReconciliation();
232 }
233
234 void AccountReconcilor::AddMergeSessionObserver(
235 MergeSessionHelper::Observer* observer) {
236 merge_session_helper_.AddObserver(observer);
237 }
238
239 void AccountReconcilor::RemoveMergeSessionObserver(
240 MergeSessionHelper::Observer* observer) {
241 merge_session_helper_.RemoveObserver(observer);
242 }
243
244 void AccountReconcilor::DeleteFetchers() {
245 delete[] requests_;
246 requests_ = NULL;
247
248 user_id_fetchers_.clear();
249 refresh_token_fetchers_.clear();
250 }
251
252 bool AccountReconcilor::AreAllRefreshTokensChecked() const {
253 return chrome_accounts_.size() ==
254 (valid_chrome_accounts_.size() + invalid_chrome_accounts_.size());
255 }
256
257 void AccountReconcilor::RegisterForCookieChanges() {
258 client_->SetCookieChangedCallback(
259 base::Bind(&AccountReconcilor::OnCookieChanged, base::Unretained(this)));
260 }
261
262 void AccountReconcilor::UnregisterForCookieChanges() {
263 client_->SetCookieChangedCallback(SigninClient::CookieChangedCallback());
264 }
265
266 void AccountReconcilor::RegisterWithSigninManager() {
267 SigninManagerBase* signin_manager =
268 SigninManagerFactory::GetForProfile(profile_);
269 signin_manager->AddObserver(this);
270 }
271
272 void AccountReconcilor::UnregisterWithSigninManager() {
273 SigninManagerBase* signin_manager =
274 SigninManagerFactory::GetForProfile(profile_);
275 signin_manager->RemoveObserver(this);
276 }
277
278 void AccountReconcilor::RegisterWithTokenService() {
279 VLOG(1) << "AccountReconcilor::RegisterWithTokenService";
280 // During re-auth, the reconcilor will get a callback about successful signin
281 // even when the profile is already connected. Avoid re-registering
282 // with the token service since this will DCHECK.
283 if (registered_with_token_service_)
284 return;
285
286 ProfileOAuth2TokenService* token_service =
287 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
288 token_service->AddObserver(this);
289 registered_with_token_service_ = true;
290 }
291
292 void AccountReconcilor::UnregisterWithTokenService() {
293 if (!registered_with_token_service_)
294 return;
295
296 ProfileOAuth2TokenService* token_service =
297 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
298 token_service->RemoveObserver(this);
299 registered_with_token_service_ = false;
300 }
301
302 bool AccountReconcilor::IsProfileConnected() {
303 return !SigninManagerFactory::GetForProfile(profile_)->
304 GetAuthenticatedUsername().empty();
305 }
306
307 void AccountReconcilor::StartPeriodicReconciliation() {
308 VLOG(1) << "AccountReconcilor::StartPeriodicReconciliation";
309 // TODO(rogerta): pick appropriate thread and timeout value.
310 reconciliation_timer_.Start(
311 FROM_HERE,
312 base::TimeDelta::FromSeconds(300),
313 this,
314 &AccountReconcilor::PeriodicReconciliation);
315 }
316
317 void AccountReconcilor::StopPeriodicReconciliation() {
318 VLOG(1) << "AccountReconcilor::StopPeriodicReconciliation";
319 reconciliation_timer_.Stop();
320 }
321
322 void AccountReconcilor::PeriodicReconciliation() {
323 VLOG(1) << "AccountReconcilor::PeriodicReconciliation";
324 StartReconcile();
325 }
326
327 void AccountReconcilor::OnCookieChanged(const net::CanonicalCookie* cookie) {
328 if (cookie->Name() == "LSID" &&
329 cookie->Domain() == GaiaUrls::GetInstance()->gaia_url().host() &&
330 cookie->IsSecure() && cookie->IsHttpOnly()) {
331 VLOG(1) << "AccountReconcilor::OnCookieChanged: LSID changed";
332 StartReconcile();
333 }
334 }
335
336 void AccountReconcilor::OnRefreshTokenAvailable(const std::string& account_id) {
337 VLOG(1) << "AccountReconcilor::OnRefreshTokenAvailable: " << account_id;
338 StartReconcile();
339 }
340
341 void AccountReconcilor::OnRefreshTokenRevoked(const std::string& account_id) {
342 VLOG(1) << "AccountReconcilor::OnRefreshTokenRevoked: " << account_id;
343 StartRemoveAction(account_id);
344 }
345
346 void AccountReconcilor::OnRefreshTokensLoaded() {}
347
348 void AccountReconcilor::GoogleSigninSucceeded(
349 const std::string& username, const std::string& password) {
350 VLOG(1) << "AccountReconcilor::GoogleSigninSucceeded: signed in";
351 RegisterForCookieChanges();
352 RegisterWithTokenService();
353 StartPeriodicReconciliation();
354 }
355
356 void AccountReconcilor::GoogleSignedOut(const std::string& username) {
357 VLOG(1) << "AccountReconcilor::GoogleSignedOut: signed out";
358 UnregisterWithTokenService();
359 UnregisterForCookieChanges();
360 StopPeriodicReconciliation();
361 }
362
363 void AccountReconcilor::PerformMergeAction(const std::string& account_id) {
364 VLOG(1) << "AccountReconcilor::PerformMergeAction: " << account_id;
365 merge_session_helper_.LogIn(account_id);
366 }
367
368 void AccountReconcilor::StartRemoveAction(const std::string& account_id) {
369 VLOG(1) << "AccountReconcilor::StartRemoveAction: " << account_id;
370 GetAccountsFromCookie(
371 base::Bind(&AccountReconcilor::FinishRemoveAction,
372 base::Unretained(this),
373 account_id));
374 }
375
376 void AccountReconcilor::FinishRemoveAction(
377 const std::string& account_id,
378 const GoogleServiceAuthError& error,
379 const std::vector<std::pair<std::string, bool> >& accounts) {
380 VLOG(1) << "AccountReconcilor::FinishRemoveAction:"
381 << " account=" << account_id
382 << " error=" << error.ToString();
383 if (error.state() == GoogleServiceAuthError::NONE) {
384 AbortReconcile();
385 std::vector<std::string> accounts_only;
386 for (std::vector<std::pair<std::string, bool> >::const_iterator i =
387 accounts.begin(); i != accounts.end(); ++i) {
388 accounts_only.push_back(i->first);
389 }
390 merge_session_helper_.LogOut(account_id, accounts_only);
391 }
392 // Wait for the next ReconcileAction if there is an error.
393 }
394
395 void AccountReconcilor::PerformAddToChromeAction(
396 const std::string& account_id,
397 int session_index) {
398 VLOG(1) << "AccountReconcilor::PerformAddToChromeAction:"
399 << " account=" << account_id
400 << " session_index=" << session_index;
401
402 #if !defined(OS_ANDROID) && !defined(OS_IOS)
403 refresh_token_fetchers_.push_back(
404 new RefreshTokenFetcher(this, account_id, session_index));
405 #endif
406 }
407
408 void AccountReconcilor::PerformLogoutAllAccountsAction() {
409 VLOG(1) << "AccountReconcilor::PerformLogoutAllAccountsAction";
410 merge_session_helper_.LogOutAllAccounts();
411 }
412
413 void AccountReconcilor::StartReconcile() {
414 if (!IsProfileConnected() || is_reconcile_started_)
415 return;
416
417 is_reconcile_started_ = true;
418
419 // Reset state for validating gaia cookie.
420 are_gaia_accounts_set_ = false;
421 gaia_accounts_.clear();
422 GetAccountsFromCookie(base::Bind(
423 &AccountReconcilor::ContinueReconcileActionAfterGetGaiaAccounts,
424 base::Unretained(this)));
425
426 // Reset state for validating oauth2 tokens.
427 primary_account_.clear();
428 chrome_accounts_.clear();
429 DeleteFetchers();
430 valid_chrome_accounts_.clear();
431 invalid_chrome_accounts_.clear();
432 add_to_cookie_.clear();
433 add_to_chrome_.clear();
434 ValidateAccountsFromTokenService();
435 }
436
437 void AccountReconcilor::GetAccountsFromCookie(
438 GetAccountsFromCookieCallback callback) {
439 get_gaia_accounts_callbacks_.push_back(callback);
440 if (!gaia_fetcher_) {
441 // There is no list account request in flight.
442 gaia_fetcher_.reset(new GaiaAuthFetcher(this, GaiaConstants::kChromeSource,
443 profile_->GetRequestContext()));
444 gaia_fetcher_->StartListAccounts();
445 }
446 }
447
448 void AccountReconcilor::OnListAccountsSuccess(const std::string& data) {
449 gaia_fetcher_.reset();
450
451 // Get account information from response data.
452 std::vector<std::pair<std::string, bool> > gaia_accounts;
453 bool valid_json = gaia::ParseListAccountsData(data, &gaia_accounts);
454 if (!valid_json) {
455 VLOG(1) << "AccountReconcilor::OnListAccountsSuccess: parsing error";
456 } else if (gaia_accounts.size() > 0) {
457 VLOG(1) << "AccountReconcilor::OnListAccountsSuccess: "
458 << "Gaia " << gaia_accounts.size() << " accounts, "
459 << "Primary is '" << gaia_accounts[0].first << "'";
460 } else {
461 VLOG(1) << "AccountReconcilor::OnListAccountsSuccess: No accounts";
462 }
463
464 // There must be at least one callback waiting for result.
465 DCHECK(!get_gaia_accounts_callbacks_.empty());
466
467 GoogleServiceAuthError error = !valid_json
468 ? GoogleServiceAuthError(
469 GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE)
470 : GoogleServiceAuthError::AuthErrorNone();
471 get_gaia_accounts_callbacks_.front().Run(error, gaia_accounts);
472 get_gaia_accounts_callbacks_.pop_front();
473
474 MayBeDoNextListAccounts();
475 }
476
477 void AccountReconcilor::OnListAccountsFailure(
478 const GoogleServiceAuthError& error) {
479 gaia_fetcher_.reset();
480 VLOG(1) << "AccountReconcilor::OnListAccountsFailure: " << error.ToString();
481 std::vector<std::pair<std::string, bool> > empty_accounts;
482
483 // There must be at least one callback waiting for result.
484 DCHECK(!get_gaia_accounts_callbacks_.empty());
485
486 get_gaia_accounts_callbacks_.front().Run(error, empty_accounts);
487 get_gaia_accounts_callbacks_.pop_front();
488
489 MayBeDoNextListAccounts();
490 }
491
492 void AccountReconcilor::MayBeDoNextListAccounts() {
493 if (!get_gaia_accounts_callbacks_.empty()) {
494 gaia_fetcher_.reset(new GaiaAuthFetcher(this, GaiaConstants::kChromeSource,
495 profile_->GetRequestContext()));
496 gaia_fetcher_->StartListAccounts();
497 }
498 }
499
500 void AccountReconcilor::ContinueReconcileActionAfterGetGaiaAccounts(
501 const GoogleServiceAuthError& error,
502 const std::vector<std::pair<std::string, bool> >& accounts) {
503 if (error.state() == GoogleServiceAuthError::NONE) {
504 gaia_accounts_ = accounts;
505 are_gaia_accounts_set_ = true;
506 FinishReconcile();
507 } else {
508 AbortReconcile();
509 }
510 }
511
512 void AccountReconcilor::ValidateAccountsFromTokenService() {
513 primary_account_ =
514 SigninManagerFactory::GetForProfile(profile_)->GetAuthenticatedUsername();
515 DCHECK(!primary_account_.empty());
516
517 ProfileOAuth2TokenService* token_service =
518 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
519 chrome_accounts_ = token_service->GetAccounts();
520 DCHECK_GT(chrome_accounts_.size(), 0u);
521
522 VLOG(1) << "AccountReconcilor::ValidateAccountsFromTokenService: "
523 << "Chrome " << chrome_accounts_.size() << " accounts, "
524 << "Primary is '" << primary_account_ << "'";
525
526 DCHECK(!requests_);
527 requests_ =
528 new scoped_ptr<OAuth2TokenService::Request>[chrome_accounts_.size()];
529 const OAuth2TokenService::ScopeSet scopes =
530 AccountReconcilor::UserIdFetcher::GetScopes();
531 for (size_t i = 0; i < chrome_accounts_.size(); ++i) {
532 requests_[i] = token_service->StartRequest(chrome_accounts_[i],
533 scopes,
534 this);
535 }
536
537 DCHECK_EQ(0u, user_id_fetchers_.size());
538 user_id_fetchers_.resize(chrome_accounts_.size());
539 }
540
541 void AccountReconcilor::OnGetTokenSuccess(
542 const OAuth2TokenService::Request* request,
543 const std::string& access_token,
544 const base::Time& expiration_time) {
545 size_t index;
546 for (index = 0; index < chrome_accounts_.size(); ++index) {
547 if (request == requests_[index].get())
548 break;
549 }
550 DCHECK(index < chrome_accounts_.size());
551
552 const std::string& account_id = chrome_accounts_[index];
553
554 VLOG(1) << "AccountReconcilor::OnGetTokenSuccess: valid " << account_id;
555
556 DCHECK(!user_id_fetchers_[index]);
557 user_id_fetchers_[index] =
558 new UserIdFetcher(this, access_token, account_id);
559 }
560
561 void AccountReconcilor::OnGetTokenFailure(
562 const OAuth2TokenService::Request* request,
563 const GoogleServiceAuthError& error) {
564 size_t index;
565 for (index = 0; index < chrome_accounts_.size(); ++index) {
566 if (request == requests_[index].get())
567 break;
568 }
569 DCHECK(index < chrome_accounts_.size());
570
571 const std::string& account_id = chrome_accounts_[index];
572
573 VLOG(1) << "AccountReconcilor::OnGetTokenFailure: invalid "
574 << account_id;
575 HandleFailedAccountIdCheck(account_id);
576 }
577
578 void AccountReconcilor::FinishReconcile() {
579 // Make sure that the process of validating the gaia cookie and the oauth2
580 // tokens individually is done before proceeding with reconciliation.
581 if (!are_gaia_accounts_set_ || !AreAllRefreshTokensChecked())
582 return;
583
584 VLOG(1) << "AccountReconcilor::FinishReconcile";
585
586 DeleteFetchers();
587
588 DCHECK(add_to_cookie_.empty());
589 DCHECK(add_to_chrome_.empty());
590 bool are_primaries_equal =
591 gaia_accounts_.size() > 0 &&
592 gaia::AreEmailsSame(primary_account_, gaia_accounts_[0].first);
593
594 if (are_primaries_equal) {
595 // Determine if we need to merge accounts from gaia cookie to chrome.
596 for (size_t i = 0; i < gaia_accounts_.size(); ++i) {
597 const std::string& gaia_account = gaia_accounts_[i].first;
598 if (gaia_accounts_[i].second &&
599 valid_chrome_accounts_.find(gaia_account) ==
600 valid_chrome_accounts_.end()) {
601 add_to_chrome_.push_back(std::make_pair(gaia_account, i));
602 }
603 }
604
605 // Determine if we need to merge accounts from chrome into gaia cookie.
606 for (EmailSet::const_iterator i = valid_chrome_accounts_.begin();
607 i != valid_chrome_accounts_.end(); ++i) {
608 bool add_to_cookie = true;
609 for (size_t j = 0; j < gaia_accounts_.size(); ++j) {
610 if (gaia::AreEmailsSame(gaia_accounts_[j].first, *i)) {
611 add_to_cookie = !gaia_accounts_[j].second;
612 break;
613 }
614 }
615 if (add_to_cookie)
616 add_to_cookie_.push_back(*i);
617 }
618 } else {
619 VLOG(1) << "AccountReconcilor::FinishReconcile: rebuild cookie";
620 // Really messed up state. Blow away the gaia cookie completely and
621 // rebuild it, making sure the primary account as specified by the
622 // SigninManager is the first session in the gaia cookie.
623 PerformLogoutAllAccountsAction();
624 add_to_cookie_.push_back(primary_account_);
625 for (EmailSet::const_iterator i = valid_chrome_accounts_.begin();
626 i != valid_chrome_accounts_.end(); ++i) {
627 if (*i != primary_account_)
628 add_to_cookie_.push_back(*i);
629 }
630 }
631
632 // For each account known to chrome but not in the gaia cookie,
633 // PerformMergeAction().
634 for (size_t i = 0; i < add_to_cookie_.size(); ++i)
635 PerformMergeAction(add_to_cookie_[i]);
636
637 // For each account in the gaia cookie not known to chrome,
638 // PerformAddToChromeAction.
639 for (std::vector<std::pair<std::string, int> >::const_iterator i =
640 add_to_chrome_.begin();
641 i != add_to_chrome_.end(); ++i) {
642 PerformAddToChromeAction(i->first, i->second);
643 }
644
645 CalculateIfReconcileIsDone();
646 ScheduleStartReconcileIfChromeAccountsChanged();
647 }
648
649 void AccountReconcilor::AbortReconcile() {
650 VLOG(1) << "AccountReconcilor::AbortReconcile: we'll try again later";
651 DeleteFetchers();
652 add_to_cookie_.clear();
653 add_to_chrome_.clear();
654 CalculateIfReconcileIsDone();
655 }
656
657 void AccountReconcilor::CalculateIfReconcileIsDone() {
658 is_reconcile_started_ = !add_to_cookie_.empty() || !add_to_chrome_.empty();
659 if (!is_reconcile_started_)
660 VLOG(1) << "AccountReconcilor::CalculateIfReconcileIsDone: done";
661 }
662
663 void AccountReconcilor::ScheduleStartReconcileIfChromeAccountsChanged() {
664 if (is_reconcile_started_)
665 return;
666
667 // Start a reconcile as the token accounts have changed.
668 VLOG(1) << "AccountReconcilor::StartReconcileIfChromeAccountsChanged";
669 std::vector<std::string> reconciled_accounts(chrome_accounts_);
670 std::vector<std::string> new_chrome_accounts(
671 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->GetAccounts());
672 std::sort(reconciled_accounts.begin(), reconciled_accounts.end());
673 std::sort(new_chrome_accounts.begin(), new_chrome_accounts.end());
674 if (reconciled_accounts != new_chrome_accounts) {
675 base::MessageLoop::current()->PostTask(
676 FROM_HERE,
677 base::Bind(&AccountReconcilor::StartReconcile, base::Unretained(this)));
678 }
679 }
680
681 void AccountReconcilor::MergeSessionCompleted(
682 const std::string& account_id,
683 const GoogleServiceAuthError& error) {
684 VLOG(1) << "AccountReconcilor::MergeSessionCompleted: account_id="
685 << account_id;
686
687 // Remove the account from the list that is being merged.
688 for (std::vector<std::string>::iterator i = add_to_cookie_.begin();
689 i != add_to_cookie_.end(); ++i) {
690 if (account_id == *i) {
691 add_to_cookie_.erase(i);
692 break;
693 }
694 }
695
696 CalculateIfReconcileIsDone();
697 ScheduleStartReconcileIfChromeAccountsChanged();
698 }
699
700 void AccountReconcilor::HandleSuccessfulAccountIdCheck(
701 const std::string& account_id) {
702 valid_chrome_accounts_.insert(account_id);
703 FinishReconcile();
704 }
705
706 void AccountReconcilor::HandleFailedAccountIdCheck(
707 const std::string& account_id) {
708 invalid_chrome_accounts_.insert(account_id);
709 FinishReconcile();
710 }
711
712 void AccountReconcilor::HandleRefreshTokenFetched(
713 const std::string& account_id,
714 const std::string& refresh_token) {
715 if (!refresh_token.empty()) {
716 ProfileOAuth2TokenService* token_service =
717 ProfileOAuth2TokenServiceFactory::GetForProfile(profile());
718 token_service->UpdateCredentials(account_id, refresh_token);
719 }
720
721 // Remove the account from the list that is being updated.
722 for (std::vector<std::pair<std::string, int> >::iterator i =
723 add_to_chrome_.begin();
724 i != add_to_chrome_.end(); ++i) {
725 if (gaia::AreEmailsSame(account_id, i->first)) {
726 add_to_chrome_.erase(i);
727 break;
728 }
729 }
730
731 CalculateIfReconcileIsDone();
732 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698