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