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.
5 #include "components/signin/core/browser/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 "components/signin/core/browser/profile_oauth2_token_service.h"
17 #include "components/signin/core/browser/signin_client.h"
18 #include "components/signin/core/browser/signin_oauth_helper.h"
19 #include "google_apis/gaia/gaia_auth_fetcher.h"
20 #include "google_apis/gaia/gaia_auth_util.h"
21 #include "google_apis/gaia/gaia_constants.h"
22 #include "google_apis/gaia/gaia_oauth_client.h"
23 #include "google_apis/gaia/gaia_urls.h"
24 #include "net/cookies/canonical_cookie.h"
29 class EmailEqualToFunc : public std::equal_to<std::pair<std::string, bool> > {
31 bool operator()(const std::pair<std::string, bool>& p1,
32 const std::pair<std::string, bool>& p2) const;
35 bool EmailEqualToFunc::operator()(
36 const std::pair<std::string, bool>& p1,
37 const std::pair<std::string, bool>& p2) const {
38 return p1.second == p2.second && gaia::AreEmailsSame(p1.first, p2.first);
44 // Fetches a refresh token from the given session in the GAIA cookie. This is
45 // a best effort only. If it should fail, another reconcile action will occur
47 class AccountReconcilor::RefreshTokenFetcher
48 : public SigninOAuthHelper,
49 public SigninOAuthHelper::Consumer {
51 RefreshTokenFetcher(AccountReconcilor* reconcilor,
52 const std::string& account_id,
54 virtual ~RefreshTokenFetcher() {}
57 // Overridden from GaiaAuthConsumer:
58 virtual void OnSigninOAuthInformationAvailable(
59 const std::string& email,
60 const std::string& display_email,
61 const std::string& refresh_token) OVERRIDE;
63 // Called when an error occurs while getting the information.
64 virtual void OnSigninOAuthInformationFailure(
65 const GoogleServiceAuthError& error) OVERRIDE;
67 AccountReconcilor* reconcilor_;
68 const std::string account_id_;
71 DISALLOW_COPY_AND_ASSIGN(RefreshTokenFetcher);
74 AccountReconcilor::RefreshTokenFetcher::RefreshTokenFetcher(
75 AccountReconcilor* reconcilor,
76 const std::string& account_id,
78 : SigninOAuthHelper(reconcilor->client()->GetURLRequestContext(),
79 base::IntToString(session_index),
81 reconcilor_(reconcilor),
82 account_id_(account_id),
83 session_index_(session_index) {
85 DCHECK(!account_id.empty());
88 void AccountReconcilor::RefreshTokenFetcher::OnSigninOAuthInformationAvailable(
89 const std::string& email,
90 const std::string& display_email,
91 const std::string& refresh_token) {
92 VLOG(1) << "RefreshTokenFetcher::OnSigninOAuthInformationAvailable:"
93 << " account=" << account_id_ << " email=" << email
94 << " displayEmail=" << display_email;
96 // TODO(rogerta): because of the problem with email vs displayEmail and
97 // emails that have been canonicalized, the argument |email| is used here
98 // to make sure the correct string is used when calling the token service.
99 // This will be cleaned up when chrome moves to using gaia obfuscated id.
100 reconcilor_->HandleRefreshTokenFetched(email, refresh_token);
103 void AccountReconcilor::RefreshTokenFetcher::OnSigninOAuthInformationFailure(
104 const GoogleServiceAuthError& error) {
105 VLOG(1) << "RefreshTokenFetcher::OnSigninOAuthInformationFailure:"
106 << " account=" << account_id_ << " session_index=" << session_index_;
107 reconcilor_->HandleRefreshTokenFetched(account_id_, std::string());
110 bool AccountReconcilor::EmailLessFunc::operator()(const std::string& s1,
111 const std::string& s2) const {
112 return gaia::CanonicalizeEmail(s1) < gaia::CanonicalizeEmail(s2);
115 class AccountReconcilor::UserIdFetcher
116 : public gaia::GaiaOAuthClient::Delegate {
118 UserIdFetcher(AccountReconcilor* reconcilor,
119 const std::string& access_token,
120 const std::string& account_id);
122 // Returns the scopes needed by the UserIdFetcher.
123 static OAuth2TokenService::ScopeSet GetScopes();
126 // Overriden from gaia::GaiaOAuthClient::Delegate.
127 virtual void OnGetUserIdResponse(const std::string& user_id) OVERRIDE;
128 virtual void OnOAuthError() OVERRIDE;
129 virtual void OnNetworkError(int response_code) OVERRIDE;
131 AccountReconcilor* const reconcilor_;
132 const std::string account_id_;
133 const std::string access_token_;
134 gaia::GaiaOAuthClient gaia_auth_client_;
136 DISALLOW_COPY_AND_ASSIGN(UserIdFetcher);
139 AccountReconcilor::UserIdFetcher::UserIdFetcher(AccountReconcilor* reconcilor,
140 const std::string& access_token,
141 const std::string& account_id)
142 : reconcilor_(reconcilor),
143 account_id_(account_id),
144 access_token_(access_token),
145 gaia_auth_client_(reconcilor_->client()->GetURLRequestContext()) {
147 DCHECK(!account_id_.empty());
149 const int kMaxRetries = 5;
150 gaia_auth_client_.GetUserId(access_token_, kMaxRetries, this);
154 OAuth2TokenService::ScopeSet AccountReconcilor::UserIdFetcher::GetScopes() {
155 OAuth2TokenService::ScopeSet scopes;
156 scopes.insert("https://www.googleapis.com/auth/userinfo.profile");
160 void AccountReconcilor::UserIdFetcher::OnGetUserIdResponse(
161 const std::string& user_id) {
162 VLOG(1) << "AccountReconcilor::OnGetUserIdResponse: " << account_id_;
164 // HandleSuccessfulAccountIdCheck() may delete |this|, so call it last.
165 reconcilor_->HandleSuccessfulAccountIdCheck(account_id_);
168 void AccountReconcilor::UserIdFetcher::OnOAuthError() {
169 VLOG(1) << "AccountReconcilor::OnOAuthError: " << account_id_;
171 // Invalidate the access token to force a refetch next time.
172 reconcilor_->token_service()->InvalidateToken(
173 account_id_, GetScopes(), access_token_);
175 // HandleFailedAccountIdCheck() may delete |this|, so call it last.
176 reconcilor_->HandleFailedAccountIdCheck(account_id_);
179 void AccountReconcilor::UserIdFetcher::OnNetworkError(int response_code) {
180 VLOG(1) << "AccountReconcilor::OnNetworkError: " << account_id_
181 << " response_code=" << response_code;
183 // TODO(rogerta): some response error should not be treated like
184 // permanent errors. Figure out appropriate ones.
185 // HandleFailedAccountIdCheck() may delete |this|, so call it last.
186 reconcilor_->HandleFailedAccountIdCheck(account_id_);
189 AccountReconcilor::AccountReconcilor(ProfileOAuth2TokenService* token_service,
190 SigninManagerBase* signin_manager,
191 SigninClient* client)
192 : OAuth2TokenService::Consumer("account_reconcilor"),
193 token_service_(token_service),
194 signin_manager_(signin_manager),
196 merge_session_helper_(token_service_,
197 client->GetURLRequestContext(),
199 registered_with_token_service_(false),
200 is_reconcile_started_(false),
201 are_gaia_accounts_set_(false),
203 VLOG(1) << "AccountReconcilor::AccountReconcilor";
206 AccountReconcilor::~AccountReconcilor() {
207 VLOG(1) << "AccountReconcilor::~AccountReconcilor";
208 // Make sure shutdown was called first.
209 DCHECK(!registered_with_token_service_);
211 DCHECK_EQ(0u, user_id_fetchers_.size());
212 DCHECK_EQ(0u, refresh_token_fetchers_.size());
215 void AccountReconcilor::Initialize(bool start_reconcile_if_tokens_available) {
216 VLOG(1) << "AccountReconcilor::Initialize";
217 RegisterWithSigninManager();
219 // If this user is not signed in, the reconcilor should do nothing but
221 if (IsProfileConnected()) {
222 RegisterForCookieChanges();
223 RegisterWithTokenService();
225 // Start a reconcile if the tokens are already loaded.
226 if (start_reconcile_if_tokens_available &&
227 token_service_->GetAccounts().size() > 0) {
233 void AccountReconcilor::Shutdown() {
234 VLOG(1) << "AccountReconcilor::Shutdown";
235 merge_session_helper_.CancelAll();
236 merge_session_helper_.RemoveObserver(this);
237 gaia_fetcher_.reset();
239 UnregisterWithSigninManager();
240 UnregisterWithTokenService();
241 UnregisterForCookieChanges();
244 void AccountReconcilor::AddMergeSessionObserver(
245 MergeSessionHelper::Observer* observer) {
246 merge_session_helper_.AddObserver(observer);
249 void AccountReconcilor::RemoveMergeSessionObserver(
250 MergeSessionHelper::Observer* observer) {
251 merge_session_helper_.RemoveObserver(observer);
254 void AccountReconcilor::DeleteFetchers() {
258 user_id_fetchers_.clear();
259 refresh_token_fetchers_.clear();
262 bool AccountReconcilor::AreAllRefreshTokensChecked() const {
263 return chrome_accounts_.size() ==
264 (valid_chrome_accounts_.size() + invalid_chrome_accounts_.size());
267 void AccountReconcilor::RegisterForCookieChanges() {
268 // First clear any existing registration to avoid DCHECKs that can otherwise
269 // go off in some embedders on reauth (e.g., ChromeSigninClient).
270 UnregisterForCookieChanges();
271 client_->SetCookieChangedCallback(
272 base::Bind(&AccountReconcilor::OnCookieChanged, base::Unretained(this)));
275 void AccountReconcilor::UnregisterForCookieChanges() {
276 client_->SetCookieChangedCallback(SigninClient::CookieChangedCallback());
279 void AccountReconcilor::RegisterWithSigninManager() {
280 signin_manager_->AddObserver(this);
283 void AccountReconcilor::UnregisterWithSigninManager() {
284 signin_manager_->RemoveObserver(this);
287 void AccountReconcilor::RegisterWithTokenService() {
288 VLOG(1) << "AccountReconcilor::RegisterWithTokenService";
289 // During re-auth, the reconcilor will get a callback about successful signin
290 // even when the profile is already connected. Avoid re-registering
291 // with the token service since this will DCHECK.
292 if (registered_with_token_service_)
295 token_service_->AddObserver(this);
296 registered_with_token_service_ = true;
299 void AccountReconcilor::UnregisterWithTokenService() {
300 if (!registered_with_token_service_)
303 token_service_->RemoveObserver(this);
304 registered_with_token_service_ = false;
307 bool AccountReconcilor::IsProfileConnected() {
308 return !signin_manager_->GetAuthenticatedUsername().empty();
311 void AccountReconcilor::OnCookieChanged(const net::CanonicalCookie* cookie) {
312 if (cookie->Name() == "LSID" &&
313 cookie->Domain() == GaiaUrls::GetInstance()->gaia_url().host() &&
314 cookie->IsSecure() && cookie->IsHttpOnly()) {
315 VLOG(1) << "AccountReconcilor::OnCookieChanged: LSID changed";
317 // On Chrome OS it is possible that O2RT is not available at this moment
318 // because profile data transfer is still in progress.
319 if (!token_service_->GetAccounts().size()) {
320 VLOG(1) << "AccountReconcilor::OnCookieChanged: cookie change is ingored"
321 "because profile data transfer is in progress.";
329 void AccountReconcilor::OnRefreshTokenAvailable(const std::string& account_id) {
330 VLOG(1) << "AccountReconcilor::OnRefreshTokenAvailable: " << account_id;
334 void AccountReconcilor::OnRefreshTokenRevoked(const std::string& account_id) {
335 VLOG(1) << "AccountReconcilor::OnRefreshTokenRevoked: " << account_id;
336 StartRemoveAction(account_id);
339 void AccountReconcilor::OnRefreshTokensLoaded() {}
341 void AccountReconcilor::GoogleSigninSucceeded(const std::string& username,
342 const std::string& password) {
343 VLOG(1) << "AccountReconcilor::GoogleSigninSucceeded: signed in";
344 RegisterForCookieChanges();
345 RegisterWithTokenService();
348 void AccountReconcilor::GoogleSignedOut(const std::string& username) {
349 VLOG(1) << "AccountReconcilor::GoogleSignedOut: signed out";
350 UnregisterWithTokenService();
351 UnregisterForCookieChanges();
354 void AccountReconcilor::PerformMergeAction(const std::string& account_id) {
355 VLOG(1) << "AccountReconcilor::PerformMergeAction: " << account_id;
356 merge_session_helper_.LogIn(account_id);
359 void AccountReconcilor::StartRemoveAction(const std::string& account_id) {
360 VLOG(1) << "AccountReconcilor::StartRemoveAction: " << account_id;
361 GetAccountsFromCookie(base::Bind(&AccountReconcilor::FinishRemoveAction,
362 base::Unretained(this),
366 void AccountReconcilor::FinishRemoveAction(
367 const std::string& account_id,
368 const GoogleServiceAuthError& error,
369 const std::vector<std::pair<std::string, bool> >& accounts) {
370 VLOG(1) << "AccountReconcilor::FinishRemoveAction:"
371 << " account=" << account_id << " error=" << error.ToString();
372 if (error.state() == GoogleServiceAuthError::NONE) {
374 std::vector<std::string> accounts_only;
375 for (std::vector<std::pair<std::string, bool> >::const_iterator i =
379 accounts_only.push_back(i->first);
381 merge_session_helper_.LogOut(account_id, accounts_only);
383 // Wait for the next ReconcileAction if there is an error.
386 void AccountReconcilor::PerformAddToChromeAction(const std::string& account_id,
388 VLOG(1) << "AccountReconcilor::PerformAddToChromeAction:"
389 << " account=" << account_id << " session_index=" << session_index;
391 #if !defined(OS_ANDROID) && !defined(OS_IOS)
392 refresh_token_fetchers_.push_back(
393 new RefreshTokenFetcher(this, account_id, session_index));
397 void AccountReconcilor::PerformLogoutAllAccountsAction() {
398 VLOG(1) << "AccountReconcilor::PerformLogoutAllAccountsAction";
399 merge_session_helper_.LogOutAllAccounts();
402 void AccountReconcilor::StartReconcile() {
403 if (!IsProfileConnected() || is_reconcile_started_)
406 is_reconcile_started_ = true;
408 // Reset state for validating gaia cookie.
409 are_gaia_accounts_set_ = false;
410 gaia_accounts_.clear();
411 GetAccountsFromCookie(base::Bind(
412 &AccountReconcilor::ContinueReconcileActionAfterGetGaiaAccounts,
413 base::Unretained(this)));
415 // Reset state for validating oauth2 tokens.
416 primary_account_.clear();
417 chrome_accounts_.clear();
419 valid_chrome_accounts_.clear();
420 invalid_chrome_accounts_.clear();
421 add_to_cookie_.clear();
422 add_to_chrome_.clear();
423 ValidateAccountsFromTokenService();
426 void AccountReconcilor::GetAccountsFromCookie(
427 GetAccountsFromCookieCallback callback) {
428 get_gaia_accounts_callbacks_.push_back(callback);
429 if (!gaia_fetcher_) {
430 // There is no list account request in flight.
431 gaia_fetcher_.reset(new GaiaAuthFetcher(
432 this, GaiaConstants::kChromeSource, client_->GetURLRequestContext()));
433 gaia_fetcher_->StartListAccounts();
437 void AccountReconcilor::OnListAccountsSuccess(const std::string& data) {
438 gaia_fetcher_.reset();
440 // Get account information from response data.
441 std::vector<std::pair<std::string, bool> > gaia_accounts;
442 bool valid_json = gaia::ParseListAccountsData(data, &gaia_accounts);
444 VLOG(1) << "AccountReconcilor::OnListAccountsSuccess: parsing error";
445 } else if (gaia_accounts.size() > 0) {
446 VLOG(1) << "AccountReconcilor::OnListAccountsSuccess: "
447 << "Gaia " << gaia_accounts.size() << " accounts, "
448 << "Primary is '" << gaia_accounts[0].first << "'";
450 VLOG(1) << "AccountReconcilor::OnListAccountsSuccess: No accounts";
453 // There must be at least one callback waiting for result.
454 DCHECK(!get_gaia_accounts_callbacks_.empty());
456 GoogleServiceAuthError error =
457 !valid_json ? GoogleServiceAuthError(
458 GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE)
459 : GoogleServiceAuthError::AuthErrorNone();
460 get_gaia_accounts_callbacks_.front().Run(error, gaia_accounts);
461 get_gaia_accounts_callbacks_.pop_front();
463 MayBeDoNextListAccounts();
466 void AccountReconcilor::OnListAccountsFailure(
467 const GoogleServiceAuthError& error) {
468 gaia_fetcher_.reset();
469 VLOG(1) << "AccountReconcilor::OnListAccountsFailure: " << error.ToString();
470 std::vector<std::pair<std::string, bool> > empty_accounts;
472 // There must be at least one callback waiting for result.
473 DCHECK(!get_gaia_accounts_callbacks_.empty());
475 get_gaia_accounts_callbacks_.front().Run(error, empty_accounts);
476 get_gaia_accounts_callbacks_.pop_front();
478 MayBeDoNextListAccounts();
481 void AccountReconcilor::MayBeDoNextListAccounts() {
482 if (!get_gaia_accounts_callbacks_.empty()) {
483 gaia_fetcher_.reset(new GaiaAuthFetcher(
484 this, GaiaConstants::kChromeSource, client_->GetURLRequestContext()));
485 gaia_fetcher_->StartListAccounts();
489 void AccountReconcilor::ContinueReconcileActionAfterGetGaiaAccounts(
490 const GoogleServiceAuthError& error,
491 const std::vector<std::pair<std::string, bool> >& accounts) {
492 if (error.state() == GoogleServiceAuthError::NONE) {
493 gaia_accounts_ = accounts;
494 are_gaia_accounts_set_ = true;
501 void AccountReconcilor::ValidateAccountsFromTokenService() {
502 primary_account_ = signin_manager_->GetAuthenticatedUsername();
503 DCHECK(!primary_account_.empty());
505 chrome_accounts_ = token_service_->GetAccounts();
506 DCHECK_GT(chrome_accounts_.size(), 0u);
508 VLOG(1) << "AccountReconcilor::ValidateAccountsFromTokenService: "
509 << "Chrome " << chrome_accounts_.size() << " accounts, "
510 << "Primary is '" << primary_account_ << "'";
514 new scoped_ptr<OAuth2TokenService::Request>[chrome_accounts_.size()];
515 const OAuth2TokenService::ScopeSet scopes =
516 AccountReconcilor::UserIdFetcher::GetScopes();
517 for (size_t i = 0; i < chrome_accounts_.size(); ++i) {
519 token_service_->StartRequest(chrome_accounts_[i], scopes, this);
522 DCHECK_EQ(0u, user_id_fetchers_.size());
523 user_id_fetchers_.resize(chrome_accounts_.size());
526 void AccountReconcilor::OnGetTokenSuccess(
527 const OAuth2TokenService::Request* request,
528 const std::string& access_token,
529 const base::Time& expiration_time) {
531 for (index = 0; index < chrome_accounts_.size(); ++index) {
532 if (request == requests_[index].get())
535 DCHECK(index < chrome_accounts_.size());
537 const std::string& account_id = chrome_accounts_[index];
539 VLOG(1) << "AccountReconcilor::OnGetTokenSuccess: valid " << account_id;
541 DCHECK(!user_id_fetchers_[index]);
542 user_id_fetchers_[index] = new UserIdFetcher(this, access_token, account_id);
545 void AccountReconcilor::OnGetTokenFailure(
546 const OAuth2TokenService::Request* request,
547 const GoogleServiceAuthError& error) {
549 for (index = 0; index < chrome_accounts_.size(); ++index) {
550 if (request == requests_[index].get())
553 DCHECK(index < chrome_accounts_.size());
555 const std::string& account_id = chrome_accounts_[index];
557 VLOG(1) << "AccountReconcilor::OnGetTokenFailure: invalid " << account_id;
558 HandleFailedAccountIdCheck(account_id);
561 void AccountReconcilor::FinishReconcile() {
562 // Make sure that the process of validating the gaia cookie and the oauth2
563 // tokens individually is done before proceeding with reconciliation.
564 if (!are_gaia_accounts_set_ || !AreAllRefreshTokensChecked())
567 VLOG(1) << "AccountReconcilor::FinishReconcile";
571 DCHECK(add_to_cookie_.empty());
572 DCHECK(add_to_chrome_.empty());
573 bool are_primaries_equal =
574 gaia_accounts_.size() > 0 &&
575 gaia::AreEmailsSame(primary_account_, gaia_accounts_[0].first);
577 if (are_primaries_equal) {
578 // Determine if we need to merge accounts from gaia cookie to chrome.
579 for (size_t i = 0; i < gaia_accounts_.size(); ++i) {
580 const std::string& gaia_account = gaia_accounts_[i].first;
581 if (gaia_accounts_[i].second &&
582 valid_chrome_accounts_.find(gaia_account) ==
583 valid_chrome_accounts_.end()) {
584 add_to_chrome_.push_back(std::make_pair(gaia_account, i));
588 VLOG(1) << "AccountReconcilor::FinishReconcile: rebuild cookie";
589 // Really messed up state. Blow away the gaia cookie completely and
590 // rebuild it, making sure the primary account as specified by the
591 // SigninManager is the first session in the gaia cookie.
592 PerformLogoutAllAccountsAction();
593 gaia_accounts_.clear();
596 // Create a list of accounts that need to be added to the gaia cookie.
597 // The primary account must be first to make sure it becomes the default
598 // account in the case where chrome is completely rebuilding the cookie.
599 add_to_cookie_.push_back(primary_account_);
600 for (EmailSet::const_iterator i = valid_chrome_accounts_.begin();
601 i != valid_chrome_accounts_.end();
603 if (*i != primary_account_)
604 add_to_cookie_.push_back(*i);
607 // For each account known to chrome, PerformMergeAction() if the account is
608 // not already in the cookie jar or its state is invalid, or signal merge
609 // completed otherwise. Make a copy of |add_to_cookie_| since calls to
610 // SignalComplete() will change the array.
611 std::vector<std::string> add_to_cookie_copy = add_to_cookie_;
612 for (size_t i = 0; i < add_to_cookie_copy.size(); ++i) {
613 if (gaia_accounts_.end() !=
614 std::find_if(gaia_accounts_.begin(),
615 gaia_accounts_.end(),
616 std::bind1st(EmailEqualToFunc(),
617 std::make_pair(add_to_cookie_copy[i],
619 merge_session_helper_.SignalComplete(
620 add_to_cookie_copy[i],
621 GoogleServiceAuthError::AuthErrorNone());
623 PerformMergeAction(add_to_cookie_copy[i]);
627 // For each account in the gaia cookie not known to chrome,
628 // PerformAddToChromeAction.
629 for (std::vector<std::pair<std::string, int> >::const_iterator i =
630 add_to_chrome_.begin();
631 i != add_to_chrome_.end();
633 PerformAddToChromeAction(i->first, i->second);
636 CalculateIfReconcileIsDone();
637 ScheduleStartReconcileIfChromeAccountsChanged();
640 void AccountReconcilor::AbortReconcile() {
641 VLOG(1) << "AccountReconcilor::AbortReconcile: we'll try again later";
643 add_to_cookie_.clear();
644 add_to_chrome_.clear();
645 CalculateIfReconcileIsDone();
648 void AccountReconcilor::CalculateIfReconcileIsDone() {
649 is_reconcile_started_ = !add_to_cookie_.empty() || !add_to_chrome_.empty();
650 if (!is_reconcile_started_)
651 VLOG(1) << "AccountReconcilor::CalculateIfReconcileIsDone: done";
654 void AccountReconcilor::ScheduleStartReconcileIfChromeAccountsChanged() {
655 if (is_reconcile_started_)
658 // Start a reconcile as the token accounts have changed.
659 VLOG(1) << "AccountReconcilor::StartReconcileIfChromeAccountsChanged";
660 std::vector<std::string> reconciled_accounts(chrome_accounts_);
661 std::vector<std::string> new_chrome_accounts(token_service_->GetAccounts());
662 std::sort(reconciled_accounts.begin(), reconciled_accounts.end());
663 std::sort(new_chrome_accounts.begin(), new_chrome_accounts.end());
664 if (reconciled_accounts != new_chrome_accounts) {
665 base::MessageLoop::current()->PostTask(
667 base::Bind(&AccountReconcilor::StartReconcile, base::Unretained(this)));
671 void AccountReconcilor::MergeSessionCompleted(
672 const std::string& account_id,
673 const GoogleServiceAuthError& error) {
674 VLOG(1) << "AccountReconcilor::MergeSessionCompleted: account_id="
677 // Remove the account from the list that is being merged.
678 for (std::vector<std::string>::iterator i = add_to_cookie_.begin();
679 i != add_to_cookie_.end();
681 if (account_id == *i) {
682 add_to_cookie_.erase(i);
687 CalculateIfReconcileIsDone();
688 ScheduleStartReconcileIfChromeAccountsChanged();
691 void AccountReconcilor::HandleSuccessfulAccountIdCheck(
692 const std::string& account_id) {
693 valid_chrome_accounts_.insert(account_id);
697 void AccountReconcilor::HandleFailedAccountIdCheck(
698 const std::string& account_id) {
699 invalid_chrome_accounts_.insert(account_id);
703 void AccountReconcilor::HandleRefreshTokenFetched(
704 const std::string& account_id,
705 const std::string& refresh_token) {
706 if (!refresh_token.empty()) {
707 token_service_->UpdateCredentials(account_id, refresh_token);
710 // Remove the account from the list that is being updated.
711 for (std::vector<std::pair<std::string, int> >::iterator i =
712 add_to_chrome_.begin();
713 i != add_to_chrome_.end();
715 if (gaia::AreEmailsSame(account_id, i->first)) {
716 add_to_chrome_.erase(i);
721 CalculateIfReconcileIsDone();