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.
5 #include "chrome/browser/signin/account_reconcilor.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/chrome_notification_types.h"
17 #include "chrome/browser/net/chrome_cookie_notification_details.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
20 #include "chrome/browser/signin/signin_manager_factory.h"
21 #include "chrome/browser/signin/signin_oauth_helper.h"
22 #include "components/signin/core/browser/profile_oauth2_token_service.h"
23 #include "content/public/browser/notification_details.h"
24 #include "content/public/browser/notification_source.h"
25 #include "google_apis/gaia/gaia_auth_fetcher.h"
26 #include "google_apis/gaia/gaia_auth_util.h"
27 #include "google_apis/gaia/gaia_constants.h"
28 #include "google_apis/gaia/gaia_oauth_client.h"
29 #include "google_apis/gaia/gaia_urls.h"
31 // Fetches a refresh token from the given session in the GAIA cookie. This is
32 // a best effort only. If it should fail, another reconcile action will occur
34 class AccountReconcilor::RefreshTokenFetcher
35 : public SigninOAuthHelper,
36 public SigninOAuthHelper::Consumer {
38 RefreshTokenFetcher(AccountReconcilor* reconcilor,
39 const std::string& account_id,
41 virtual ~RefreshTokenFetcher() {}
44 // Overridden from GaiaAuthConsumer:
45 virtual void OnSigninOAuthInformationAvailable(
46 const std::string& email,
47 const std::string& display_email,
48 const std::string& refresh_token) OVERRIDE;
50 // Called when an error occurs while getting the information.
51 virtual void OnSigninOAuthInformationFailure(
52 const GoogleServiceAuthError& error) OVERRIDE;
54 AccountReconcilor* reconcilor_;
55 const std::string account_id_;
58 DISALLOW_COPY_AND_ASSIGN(RefreshTokenFetcher);
61 AccountReconcilor::RefreshTokenFetcher::RefreshTokenFetcher(
62 AccountReconcilor* reconcilor,
63 const std::string& account_id,
65 : SigninOAuthHelper(reconcilor->profile()->GetRequestContext(),
66 base::IntToString(session_index), this),
67 reconcilor_(reconcilor),
68 account_id_(account_id),
69 session_index_(session_index) {
71 DCHECK(!account_id.empty());
74 void AccountReconcilor::RefreshTokenFetcher::OnSigninOAuthInformationAvailable(
75 const std::string& email,
76 const std::string& display_email,
77 const std::string& refresh_token) {
78 VLOG(1) << "RefreshTokenFetcher::OnSigninOAuthInformationAvailable:"
79 << " account=" << account_id_
81 << " displayEmail=" << display_email;
83 // TODO(rogerta): because of the problem with email vs displayEmail and
84 // emails that have been canonicalized, the argument |email| is used here
85 // to make sure the correct string is used when calling the token service.
86 // This will be cleaned up when chrome moves to using gaia obfuscated id.
87 reconcilor_->HandleRefreshTokenFetched(email, refresh_token);
90 void AccountReconcilor::RefreshTokenFetcher::OnSigninOAuthInformationFailure(
91 const GoogleServiceAuthError& error) {
92 VLOG(1) << "RefreshTokenFetcher::OnSigninOAuthInformationFailure:"
93 << " account=" << account_id_
94 << " session_index=" << session_index_;
95 reconcilor_->HandleRefreshTokenFetched(account_id_, std::string());
99 bool AccountReconcilor::EmailLessFunc::operator()(const std::string& s1,
100 const std::string& s2) const {
101 return gaia::CanonicalizeEmail(s1) < gaia::CanonicalizeEmail(s2);
104 class AccountReconcilor::UserIdFetcher
105 : public gaia::GaiaOAuthClient::Delegate {
107 UserIdFetcher(AccountReconcilor* reconcilor,
108 const std::string& access_token,
109 const std::string& account_id);
111 // Returns the scopes needed by the UserIdFetcher.
112 static OAuth2TokenService::ScopeSet GetScopes();
115 // Overriden from gaia::GaiaOAuthClient::Delegate.
116 virtual void OnGetUserIdResponse(const std::string& user_id) OVERRIDE;
117 virtual void OnOAuthError() OVERRIDE;
118 virtual void OnNetworkError(int response_code) OVERRIDE;
120 AccountReconcilor* const reconcilor_;
121 const std::string account_id_;
122 const std::string access_token_;
123 gaia::GaiaOAuthClient gaia_auth_client_;
125 DISALLOW_COPY_AND_ASSIGN(UserIdFetcher);
128 AccountReconcilor::UserIdFetcher::UserIdFetcher(AccountReconcilor* reconcilor,
129 const std::string& access_token,
130 const std::string& account_id)
131 : reconcilor_(reconcilor),
132 account_id_(account_id),
133 access_token_(access_token),
134 gaia_auth_client_(reconcilor_->profile()->GetRequestContext()) {
136 DCHECK(!account_id_.empty());
138 const int kMaxRetries = 5;
139 gaia_auth_client_.GetUserId(access_token_, kMaxRetries, this);
143 OAuth2TokenService::ScopeSet AccountReconcilor::UserIdFetcher::GetScopes() {
144 OAuth2TokenService::ScopeSet scopes;
145 scopes.insert("https://www.googleapis.com/auth/userinfo.profile");
149 void AccountReconcilor::UserIdFetcher::OnGetUserIdResponse(
150 const std::string& user_id) {
151 VLOG(1) << "AccountReconcilor::OnGetUserIdResponse: " << account_id_;
153 // HandleSuccessfulAccountIdCheck() may delete |this|, so call it last.
154 reconcilor_->HandleSuccessfulAccountIdCheck(account_id_);
157 void AccountReconcilor::UserIdFetcher::OnOAuthError() {
158 VLOG(1) << "AccountReconcilor::OnOAuthError: " << account_id_;
160 // Invalidate the access token to force a refetch next time.
161 ProfileOAuth2TokenService* token_service =
162 ProfileOAuth2TokenServiceFactory::GetForProfile(reconcilor_->profile());
163 token_service->InvalidateToken(account_id_, GetScopes(), access_token_);
165 // HandleFailedAccountIdCheck() may delete |this|, so call it last.
166 reconcilor_->HandleFailedAccountIdCheck(account_id_);
169 void AccountReconcilor::UserIdFetcher::OnNetworkError(int response_code) {
170 VLOG(1) << "AccountReconcilor::OnNetworkError: " << account_id_
171 << " response_code=" << response_code;
173 // TODO(rogerta): some response error should not be treated like
174 // permanent errors. Figure out appropriate ones.
175 // HandleFailedAccountIdCheck() may delete |this|, so call it last.
176 reconcilor_->HandleFailedAccountIdCheck(account_id_);
179 AccountReconcilor::AccountReconcilor(Profile* profile)
180 : OAuth2TokenService::Consumer("account_reconcilor"),
182 merge_session_helper_(
183 ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
184 profile->GetRequestContext(),
186 registered_with_token_service_(false),
187 is_reconcile_started_(false),
188 are_gaia_accounts_set_(false),
190 VLOG(1) << "AccountReconcilor::AccountReconcilor";
193 AccountReconcilor::~AccountReconcilor() {
194 VLOG(1) << "AccountReconcilor::~AccountReconcilor";
195 // Make sure shutdown was called first.
196 DCHECK(!registered_with_token_service_);
197 DCHECK(registrar_.IsEmpty());
198 DCHECK(!reconciliation_timer_.IsRunning());
200 DCHECK_EQ(0u, user_id_fetchers_.size());
201 DCHECK_EQ(0u, refresh_token_fetchers_.size());
204 void AccountReconcilor::Initialize(bool start_reconcile_if_tokens_available) {
205 VLOG(1) << "AccountReconcilor::Initialize";
206 RegisterWithSigninManager();
208 // If this profile is not connected, the reconcilor should do nothing but
209 // wait for the connection.
210 if (IsProfileConnected()) {
211 RegisterWithCookieMonster();
212 RegisterWithTokenService();
213 StartPeriodicReconciliation();
215 // Start a reconcile if the tokens are already loaded.
216 ProfileOAuth2TokenService* token_service =
217 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
218 if (start_reconcile_if_tokens_available &&
219 token_service->GetAccounts().size() > 0) {
225 void AccountReconcilor::Shutdown() {
226 VLOG(1) << "AccountReconcilor::Shutdown";
227 merge_session_helper_.CancelAll();
228 merge_session_helper_.RemoveObserver(this);
229 gaia_fetcher_.reset();
231 UnregisterWithSigninManager();
232 UnregisterWithTokenService();
233 UnregisterWithCookieMonster();
234 StopPeriodicReconciliation();
237 void AccountReconcilor::AddMergeSessionObserver(
238 MergeSessionHelper::Observer* observer) {
239 merge_session_helper_.AddObserver(observer);
242 void AccountReconcilor::RemoveMergeSessionObserver(
243 MergeSessionHelper::Observer* observer) {
244 merge_session_helper_.RemoveObserver(observer);
247 void AccountReconcilor::DeleteFetchers() {
251 user_id_fetchers_.clear();
252 refresh_token_fetchers_.clear();
255 bool AccountReconcilor::AreAllRefreshTokensChecked() const {
256 return chrome_accounts_.size() ==
257 (valid_chrome_accounts_.size() + invalid_chrome_accounts_.size());
260 void AccountReconcilor::RegisterWithCookieMonster() {
261 content::Source<Profile> source(profile_);
262 if (!registrar_.IsRegistered(this, chrome::NOTIFICATION_COOKIE_CHANGED,
264 registrar_.Add(this, chrome::NOTIFICATION_COOKIE_CHANGED, source);
268 void AccountReconcilor::UnregisterWithCookieMonster() {
269 content::Source<Profile> source(profile_);
270 if (registrar_.IsRegistered(this, chrome::NOTIFICATION_COOKIE_CHANGED,
272 registrar_.Remove(this, chrome::NOTIFICATION_COOKIE_CHANGED, source);
276 void AccountReconcilor::RegisterWithSigninManager() {
277 SigninManagerBase* signin_manager =
278 SigninManagerFactory::GetForProfile(profile_);
279 signin_manager->AddObserver(this);
282 void AccountReconcilor::UnregisterWithSigninManager() {
283 SigninManagerBase* signin_manager =
284 SigninManagerFactory::GetForProfile(profile_);
285 signin_manager->RemoveObserver(this);
288 void AccountReconcilor::RegisterWithTokenService() {
289 VLOG(1) << "AccountReconcilor::RegisterWithTokenService";
290 // During re-auth, the reconcilor will get a GOOGLE_SIGNIN_SUCCESSFUL
291 // even when the profile is already connected. Avoid re-registering
292 // with the token service since this will DCHECK.
293 if (registered_with_token_service_)
296 ProfileOAuth2TokenService* token_service =
297 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
298 token_service->AddObserver(this);
299 registered_with_token_service_ = true;
302 void AccountReconcilor::UnregisterWithTokenService() {
303 if (!registered_with_token_service_)
306 ProfileOAuth2TokenService* token_service =
307 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
308 token_service->RemoveObserver(this);
309 registered_with_token_service_ = false;
312 bool AccountReconcilor::IsProfileConnected() {
313 return !SigninManagerFactory::GetForProfile(profile_)->
314 GetAuthenticatedUsername().empty();
317 void AccountReconcilor::StartPeriodicReconciliation() {
318 VLOG(1) << "AccountReconcilor::StartPeriodicReconciliation";
319 // TODO(rogerta): pick appropriate thread and timeout value.
320 reconciliation_timer_.Start(
322 base::TimeDelta::FromSeconds(300),
324 &AccountReconcilor::PeriodicReconciliation);
327 void AccountReconcilor::StopPeriodicReconciliation() {
328 VLOG(1) << "AccountReconcilor::StopPeriodicReconciliation";
329 reconciliation_timer_.Stop();
332 void AccountReconcilor::PeriodicReconciliation() {
333 VLOG(1) << "AccountReconcilor::PeriodicReconciliation";
337 void AccountReconcilor::Observe(int type,
338 const content::NotificationSource& source,
339 const content::NotificationDetails& details) {
341 case chrome::NOTIFICATION_COOKIE_CHANGED:
342 OnCookieChanged(content::Details<ChromeCookieDetails>(details).ptr());
350 void AccountReconcilor::OnCookieChanged(ChromeCookieDetails* details) {
351 if (details->cookie->Name() == "LSID" &&
352 details->cookie->Domain() == GaiaUrls::GetInstance()->gaia_url().host() &&
353 details->cookie->IsSecure() &&
354 details->cookie->IsHttpOnly()) {
355 VLOG(1) << "AccountReconcilor::OnCookieChanged: LSID changed";
360 void AccountReconcilor::OnRefreshTokenAvailable(const std::string& account_id) {
361 VLOG(1) << "AccountReconcilor::OnRefreshTokenAvailable: " << account_id;
365 void AccountReconcilor::OnRefreshTokenRevoked(const std::string& account_id) {
366 VLOG(1) << "AccountReconcilor::OnRefreshTokenRevoked: " << account_id;
367 StartRemoveAction(account_id);
370 void AccountReconcilor::OnRefreshTokensLoaded() {}
372 void AccountReconcilor::GoogleSigninSucceeded(
373 const std::string& username, const std::string& password) {
374 VLOG(1) << "AccountReconcilor::GoogleSigninSucceeded: signed in";
375 RegisterWithCookieMonster();
376 RegisterWithTokenService();
377 StartPeriodicReconciliation();
380 void AccountReconcilor::GoogleSignedOut(const std::string& username) {
381 VLOG(1) << "AccountReconcilor::GoogleSignedOut: signed out";
382 UnregisterWithTokenService();
383 UnregisterWithCookieMonster();
384 StopPeriodicReconciliation();
387 void AccountReconcilor::PerformMergeAction(const std::string& account_id) {
388 VLOG(1) << "AccountReconcilor::PerformMergeAction: " << account_id;
389 merge_session_helper_.LogIn(account_id);
392 void AccountReconcilor::StartRemoveAction(const std::string& account_id) {
393 VLOG(1) << "AccountReconcilor::StartRemoveAction: " << account_id;
394 GetAccountsFromCookie(
395 base::Bind(&AccountReconcilor::FinishRemoveAction,
396 base::Unretained(this),
400 void AccountReconcilor::FinishRemoveAction(
401 const std::string& account_id,
402 const GoogleServiceAuthError& error,
403 const std::vector<std::pair<std::string, bool> >& accounts) {
404 VLOG(1) << "AccountReconcilor::FinishRemoveAction:"
405 << " account=" << account_id
406 << " error=" << error.ToString();
407 if (error.state() == GoogleServiceAuthError::NONE) {
409 std::vector<std::string> accounts_only;
410 for (std::vector<std::pair<std::string, bool> >::const_iterator i =
411 accounts.begin(); i != accounts.end(); ++i) {
412 accounts_only.push_back(i->first);
414 merge_session_helper_.LogOut(account_id, accounts_only);
416 // Wait for the next ReconcileAction if there is an error.
419 void AccountReconcilor::PerformAddToChromeAction(
420 const std::string& account_id,
422 VLOG(1) << "AccountReconcilor::PerformAddToChromeAction:"
423 << " account=" << account_id
424 << " session_index=" << session_index;
426 #if !defined(OS_ANDROID) && !defined(OS_IOS)
427 refresh_token_fetchers_.push_back(
428 new RefreshTokenFetcher(this, account_id, session_index));
432 void AccountReconcilor::PerformLogoutAllAccountsAction() {
433 VLOG(1) << "AccountReconcilor::PerformLogoutAllAccountsAction";
434 merge_session_helper_.LogOutAllAccounts();
437 void AccountReconcilor::StartReconcile() {
438 if (!IsProfileConnected() || is_reconcile_started_)
441 is_reconcile_started_ = true;
443 // Reset state for validating gaia cookie.
444 are_gaia_accounts_set_ = false;
445 gaia_accounts_.clear();
446 GetAccountsFromCookie(base::Bind(
447 &AccountReconcilor::ContinueReconcileActionAfterGetGaiaAccounts,
448 base::Unretained(this)));
450 // Reset state for validating oauth2 tokens.
451 primary_account_.clear();
452 chrome_accounts_.clear();
454 valid_chrome_accounts_.clear();
455 invalid_chrome_accounts_.clear();
456 add_to_cookie_.clear();
457 add_to_chrome_.clear();
458 ValidateAccountsFromTokenService();
461 void AccountReconcilor::GetAccountsFromCookie(
462 GetAccountsFromCookieCallback callback) {
463 get_gaia_accounts_callbacks_.push_back(callback);
464 if (!gaia_fetcher_) {
465 // There is no list account request in flight.
466 gaia_fetcher_.reset(new GaiaAuthFetcher(this, GaiaConstants::kChromeSource,
467 profile_->GetRequestContext()));
468 gaia_fetcher_->StartListAccounts();
472 void AccountReconcilor::OnListAccountsSuccess(const std::string& data) {
473 gaia_fetcher_.reset();
475 // Get account information from response data.
476 std::vector<std::pair<std::string, bool> > gaia_accounts;
477 bool valid_json = gaia::ParseListAccountsData(data, &gaia_accounts);
479 VLOG(1) << "AccountReconcilor::OnListAccountsSuccess: parsing error";
480 } else if (gaia_accounts.size() > 0) {
481 VLOG(1) << "AccountReconcilor::OnListAccountsSuccess: "
482 << "Gaia " << gaia_accounts.size() << " accounts, "
483 << "Primary is '" << gaia_accounts[0].first << "'";
485 VLOG(1) << "AccountReconcilor::OnListAccountsSuccess: No accounts";
488 // There must be at least one callback waiting for result.
489 DCHECK(!get_gaia_accounts_callbacks_.empty());
491 GoogleServiceAuthError error = !valid_json
492 ? GoogleServiceAuthError(
493 GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE)
494 : GoogleServiceAuthError::AuthErrorNone();
495 get_gaia_accounts_callbacks_.front().Run(error, gaia_accounts);
496 get_gaia_accounts_callbacks_.pop_front();
498 MayBeDoNextListAccounts();
501 void AccountReconcilor::OnListAccountsFailure(
502 const GoogleServiceAuthError& error) {
503 gaia_fetcher_.reset();
504 VLOG(1) << "AccountReconcilor::OnListAccountsFailure: " << error.ToString();
505 std::vector<std::pair<std::string, bool> > empty_accounts;
507 // There must be at least one callback waiting for result.
508 DCHECK(!get_gaia_accounts_callbacks_.empty());
510 get_gaia_accounts_callbacks_.front().Run(error, empty_accounts);
511 get_gaia_accounts_callbacks_.pop_front();
513 MayBeDoNextListAccounts();
516 void AccountReconcilor::MayBeDoNextListAccounts() {
517 if (!get_gaia_accounts_callbacks_.empty()) {
518 gaia_fetcher_.reset(new GaiaAuthFetcher(this, GaiaConstants::kChromeSource,
519 profile_->GetRequestContext()));
520 gaia_fetcher_->StartListAccounts();
524 void AccountReconcilor::ContinueReconcileActionAfterGetGaiaAccounts(
525 const GoogleServiceAuthError& error,
526 const std::vector<std::pair<std::string, bool> >& accounts) {
527 if (error.state() == GoogleServiceAuthError::NONE) {
528 gaia_accounts_ = accounts;
529 are_gaia_accounts_set_ = true;
536 void AccountReconcilor::ValidateAccountsFromTokenService() {
538 SigninManagerFactory::GetForProfile(profile_)->GetAuthenticatedUsername();
539 DCHECK(!primary_account_.empty());
541 ProfileOAuth2TokenService* token_service =
542 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
543 chrome_accounts_ = token_service->GetAccounts();
544 DCHECK_GT(chrome_accounts_.size(), 0u);
546 VLOG(1) << "AccountReconcilor::ValidateAccountsFromTokenService: "
547 << "Chrome " << chrome_accounts_.size() << " accounts, "
548 << "Primary is '" << primary_account_ << "'";
552 new scoped_ptr<OAuth2TokenService::Request>[chrome_accounts_.size()];
553 const OAuth2TokenService::ScopeSet scopes =
554 AccountReconcilor::UserIdFetcher::GetScopes();
555 for (size_t i = 0; i < chrome_accounts_.size(); ++i) {
556 requests_[i] = token_service->StartRequest(chrome_accounts_[i],
561 DCHECK_EQ(0u, user_id_fetchers_.size());
562 user_id_fetchers_.resize(chrome_accounts_.size());
565 void AccountReconcilor::OnGetTokenSuccess(
566 const OAuth2TokenService::Request* request,
567 const std::string& access_token,
568 const base::Time& expiration_time) {
570 for (index = 0; index < chrome_accounts_.size(); ++index) {
571 if (request == requests_[index].get())
574 DCHECK(index < chrome_accounts_.size());
576 const std::string& account_id = chrome_accounts_[index];
578 VLOG(1) << "AccountReconcilor::OnGetTokenSuccess: valid " << account_id;
580 DCHECK(!user_id_fetchers_[index]);
581 user_id_fetchers_[index] =
582 new UserIdFetcher(this, access_token, account_id);
585 void AccountReconcilor::OnGetTokenFailure(
586 const OAuth2TokenService::Request* request,
587 const GoogleServiceAuthError& error) {
589 for (index = 0; index < chrome_accounts_.size(); ++index) {
590 if (request == requests_[index].get())
593 DCHECK(index < chrome_accounts_.size());
595 const std::string& account_id = chrome_accounts_[index];
597 VLOG(1) << "AccountReconcilor::OnGetTokenFailure: invalid "
599 HandleFailedAccountIdCheck(account_id);
602 void AccountReconcilor::FinishReconcile() {
603 // Make sure that the process of validating the gaia cookie and the oauth2
604 // tokens individually is done before proceeding with reconciliation.
605 if (!are_gaia_accounts_set_ || !AreAllRefreshTokensChecked())
608 VLOG(1) << "AccountReconcilor::FinishReconcile";
612 DCHECK(add_to_cookie_.empty());
613 DCHECK(add_to_chrome_.empty());
614 bool are_primaries_equal =
615 gaia_accounts_.size() > 0 &&
616 gaia::AreEmailsSame(primary_account_, gaia_accounts_[0].first);
618 if (are_primaries_equal) {
619 // Determine if we need to merge accounts from gaia cookie to chrome.
620 for (size_t i = 0; i < gaia_accounts_.size(); ++i) {
621 const std::string& gaia_account = gaia_accounts_[i].first;
622 if (gaia_accounts_[i].second &&
623 valid_chrome_accounts_.find(gaia_account) ==
624 valid_chrome_accounts_.end()) {
625 add_to_chrome_.push_back(std::make_pair(gaia_account, i));
629 // Determine if we need to merge accounts from chrome into gaia cookie.
630 for (EmailSet::const_iterator i = valid_chrome_accounts_.begin();
631 i != valid_chrome_accounts_.end(); ++i) {
632 bool add_to_cookie = true;
633 for (size_t j = 0; j < gaia_accounts_.size(); ++j) {
634 if (gaia::AreEmailsSame(gaia_accounts_[j].first, *i)) {
635 add_to_cookie = !gaia_accounts_[j].second;
640 add_to_cookie_.push_back(*i);
643 VLOG(1) << "AccountReconcilor::FinishReconcile: rebuild cookie";
644 // Really messed up state. Blow away the gaia cookie completely and
645 // rebuild it, making sure the primary account as specified by the
646 // SigninManager is the first session in the gaia cookie.
647 PerformLogoutAllAccountsAction();
648 add_to_cookie_.push_back(primary_account_);
649 for (EmailSet::const_iterator i = valid_chrome_accounts_.begin();
650 i != valid_chrome_accounts_.end(); ++i) {
651 if (*i != primary_account_)
652 add_to_cookie_.push_back(*i);
656 // For each account known to chrome but not in the gaia cookie,
657 // PerformMergeAction().
658 for (size_t i = 0; i < add_to_cookie_.size(); ++i)
659 PerformMergeAction(add_to_cookie_[i]);
661 // For each account in the gaia cookie not known to chrome,
662 // PerformAddToChromeAction.
663 for (std::vector<std::pair<std::string, int> >::const_iterator i =
664 add_to_chrome_.begin();
665 i != add_to_chrome_.end(); ++i) {
666 PerformAddToChromeAction(i->first, i->second);
669 CalculateIfReconcileIsDone();
670 ScheduleStartReconcileIfChromeAccountsChanged();
673 void AccountReconcilor::AbortReconcile() {
674 VLOG(1) << "AccountReconcilor::AbortReconcile: we'll try again later";
676 add_to_cookie_.clear();
677 add_to_chrome_.clear();
678 CalculateIfReconcileIsDone();
681 void AccountReconcilor::CalculateIfReconcileIsDone() {
682 is_reconcile_started_ = !add_to_cookie_.empty() || !add_to_chrome_.empty();
683 if (!is_reconcile_started_)
684 VLOG(1) << "AccountReconcilor::CalculateIfReconcileIsDone: done";
687 void AccountReconcilor::ScheduleStartReconcileIfChromeAccountsChanged() {
688 if (is_reconcile_started_)
691 // Start a reconcile as the token accounts have changed.
692 VLOG(1) << "AccountReconcilor::StartReconcileIfChromeAccountsChanged";
693 std::vector<std::string> reconciled_accounts(chrome_accounts_);
694 std::vector<std::string> new_chrome_accounts(
695 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->GetAccounts());
696 std::sort(reconciled_accounts.begin(), reconciled_accounts.end());
697 std::sort(new_chrome_accounts.begin(), new_chrome_accounts.end());
698 if (reconciled_accounts != new_chrome_accounts) {
699 base::MessageLoop::current()->PostTask(
701 base::Bind(&AccountReconcilor::StartReconcile, base::Unretained(this)));
705 void AccountReconcilor::MergeSessionCompleted(
706 const std::string& account_id,
707 const GoogleServiceAuthError& error) {
708 VLOG(1) << "AccountReconcilor::MergeSessionCompleted: account_id="
711 // Remove the account from the list that is being merged.
712 for (std::vector<std::string>::iterator i = add_to_cookie_.begin();
713 i != add_to_cookie_.end(); ++i) {
714 if (account_id == *i) {
715 add_to_cookie_.erase(i);
720 CalculateIfReconcileIsDone();
721 ScheduleStartReconcileIfChromeAccountsChanged();
724 void AccountReconcilor::HandleSuccessfulAccountIdCheck(
725 const std::string& account_id) {
726 valid_chrome_accounts_.insert(account_id);
730 void AccountReconcilor::HandleFailedAccountIdCheck(
731 const std::string& account_id) {
732 invalid_chrome_accounts_.insert(account_id);
736 void AccountReconcilor::HandleRefreshTokenFetched(
737 const std::string& account_id,
738 const std::string& refresh_token) {
739 if (!refresh_token.empty()) {
740 ProfileOAuth2TokenService* token_service =
741 ProfileOAuth2TokenServiceFactory::GetForProfile(profile());
742 token_service->UpdateCredentials(account_id, refresh_token);
745 // Remove the account from the list that is being updated.
746 for (std::vector<std::pair<std::string, int> >::iterator i =
747 add_to_chrome_.begin();
748 i != add_to_chrome_.end(); ++i) {
749 if (gaia::AreEmailsSame(account_id, i->first)) {
750 add_to_chrome_.erase(i);
755 CalculateIfReconcileIsDone();