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/gaia_cookie_manager_service.h"
10 #include "base/json/json_reader.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/time/time.h"
16 #include "base/values.h"
17 #include "components/signin/core/browser/account_tracker_service.h"
18 #include "components/signin/core/browser/signin_metrics.h"
19 #include "google_apis/gaia/gaia_auth_fetcher.h"
20 #include "google_apis/gaia/gaia_constants.h"
21 #include "google_apis/gaia/gaia_urls.h"
22 #include "google_apis/gaia/oauth2_token_service.h"
23 #include "net/base/load_flags.h"
24 #include "net/http/http_status_code.h"
25 #include "net/url_request/url_fetcher.h"
26 #include "net/url_request/url_fetcher_delegate.h"
30 // In case of an error while fetching using the GaiaAuthFetcher or URLFetcher,
31 // retry with exponential backoff. Try up to 7 times within 15 minutes.
32 const net::BackoffEntry::Policy kBackoffPolicy
= {
33 // Number of initial errors (in sequence) to ignore before applying
34 // exponential back-off rules.
37 // Initial delay for exponential backoff in ms.
40 // Factor by which the waiting time will be multiplied.
43 // Fuzzing percentage. ex: 10% will spread requests randomly
44 // between 90%-100% of the calculated time.
47 // Maximum amount of time we are willing to delay our request in ms.
48 1000 * 60 * 60 * 4, // 15 minutes.
50 // Time to keep an entry from being discarded even when it
51 // has no significant state, -1 to never discard.
54 // Don't use initial delay unless the last request was an error.
58 const int kMaxFetcherRetries
= 8;
60 enum GaiaCookieRequestType
{
69 GaiaCookieManagerService::GaiaCookieRequest::GaiaCookieRequest(
70 GaiaCookieRequestType request_type
,
71 const std::string
& account_id
)
72 : request_type_(request_type
),
73 account_id_(account_id
) {}
75 GaiaCookieManagerService::GaiaCookieRequest::~GaiaCookieRequest() {
79 GaiaCookieManagerService::GaiaCookieRequest
80 GaiaCookieManagerService::GaiaCookieRequest::CreateAddAccountRequest(
81 const std::string
& account_id
) {
82 return GaiaCookieManagerService::GaiaCookieRequest(
83 GaiaCookieManagerService::GaiaCookieRequestType::ADD_ACCOUNT
, account_id
);
87 GaiaCookieManagerService::GaiaCookieRequest
88 GaiaCookieManagerService::GaiaCookieRequest::CreateLogOutRequest() {
89 return GaiaCookieManagerService::GaiaCookieRequest(
90 GaiaCookieManagerService::GaiaCookieRequestType::LOG_OUT
, std::string());
93 GaiaCookieManagerService::GaiaCookieRequest
94 GaiaCookieManagerService::GaiaCookieRequest::CreateListAccountsRequest() {
95 return GaiaCookieManagerService::GaiaCookieRequest(
96 GaiaCookieManagerService::GaiaCookieRequestType::LIST_ACCOUNTS
,
100 GaiaCookieManagerService::ExternalCcResultFetcher::ExternalCcResultFetcher(
101 GaiaCookieManagerService
* helper
)
106 GaiaCookieManagerService::ExternalCcResultFetcher::~ExternalCcResultFetcher() {
107 CleanupTransientState();
111 GaiaCookieManagerService::ExternalCcResultFetcher::GetExternalCcResult() {
112 std::vector
<std::string
> results
;
113 for (ResultMap::const_iterator it
= results_
.begin(); it
!= results_
.end();
115 results
.push_back(it
->first
+ ":" + it
->second
);
117 return base::JoinString(results
, ",");
120 void GaiaCookieManagerService::ExternalCcResultFetcher::Start() {
121 m_external_cc_result_start_time_
= base::Time::Now();
123 CleanupTransientState();
125 helper_
->gaia_auth_fetcher_
.reset(
126 helper_
->signin_client_
->CreateGaiaAuthFetcher(
127 this, helper_
->source_
, helper_
->request_context()));
128 helper_
->gaia_auth_fetcher_
->StartGetCheckConnectionInfo();
130 // Some fetches may timeout. Start a timer to decide when the result fetcher
131 // has waited long enough.
132 // TODO(rogerta): I have no idea how long to wait before timing out.
133 // Gaia folks say this should take no more than 2 second even in mobile.
134 // This will need to be tweaked.
135 timer_
.Start(FROM_HERE
, base::TimeDelta::FromSeconds(5), this,
136 &GaiaCookieManagerService::ExternalCcResultFetcher::Timeout
);
139 bool GaiaCookieManagerService::ExternalCcResultFetcher::IsRunning() {
140 return helper_
->gaia_auth_fetcher_
|| fetchers_
.size() > 0u;
143 void GaiaCookieManagerService::ExternalCcResultFetcher::TimeoutForTests() {
147 void GaiaCookieManagerService::ExternalCcResultFetcher::
148 OnGetCheckConnectionInfoSuccess(const std::string
& data
) {
149 helper_
->fetcher_backoff_
.InformOfRequest(true);
150 gaia_auth_fetcher_timer_
.Stop();
151 scoped_ptr
<base::Value
> value
= base::JSONReader::Read(data
);
152 const base::ListValue
* list
;
153 if (!value
|| !value
->GetAsList(&list
)) {
154 CleanupTransientState();
155 GetCheckConnectionInfoCompleted(false);
159 // If there is nothing to check, terminate immediately.
160 if (list
->GetSize() == 0) {
161 CleanupTransientState();
162 GetCheckConnectionInfoCompleted(true);
166 // Start a fetcher for each connection URL that needs to be checked.
167 for (size_t i
= 0; i
< list
->GetSize(); ++i
) {
168 const base::DictionaryValue
* dict
;
169 if (list
->GetDictionary(i
, &dict
)) {
172 if (dict
->GetString("carryBackToken", &token
) &&
173 dict
->GetString("url", &url
)) {
174 results_
[token
] = "null";
175 net::URLFetcher
* fetcher
= CreateFetcher(GURL(url
)).release();
176 fetchers_
[fetcher
->GetOriginalURL()] = std::make_pair(token
, fetcher
);
183 void GaiaCookieManagerService::ExternalCcResultFetcher::
184 OnGetCheckConnectionInfoError(const GoogleServiceAuthError
& error
) {
185 if (++helper_
->fetcher_retries_
< kMaxFetcherRetries
&&
186 error
.IsTransientError()) {
187 helper_
->fetcher_backoff_
.InformOfRequest(false);
188 gaia_auth_fetcher_timer_
.Start(
189 FROM_HERE
, helper_
->fetcher_backoff_
.GetTimeUntilRelease(),
190 this, &GaiaCookieManagerService::ExternalCcResultFetcher::Start
);
194 CleanupTransientState();
195 GetCheckConnectionInfoCompleted(false);
198 scoped_ptr
<net::URLFetcher
>
199 GaiaCookieManagerService::ExternalCcResultFetcher::CreateFetcher(
201 scoped_ptr
<net::URLFetcher
> fetcher
=
202 net::URLFetcher::Create(0, url
, net::URLFetcher::GET
, this);
203 fetcher
->SetRequestContext(helper_
->request_context());
204 fetcher
->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES
|
205 net::LOAD_DO_NOT_SAVE_COOKIES
);
207 // Fetchers are sometimes cancelled because a network change was detected,
208 // especially at startup and after sign-in on ChromeOS.
209 fetcher
->SetAutomaticallyRetryOnNetworkChanges(1);
213 void GaiaCookieManagerService::ExternalCcResultFetcher::OnURLFetchComplete(
214 const net::URLFetcher
* source
) {
215 const GURL
& url
= source
->GetOriginalURL();
216 const net::URLRequestStatus
& status
= source
->GetStatus();
217 int response_code
= source
->GetResponseCode();
218 if (status
.is_success() && response_code
== net::HTTP_OK
&&
219 fetchers_
.count(url
) > 0) {
221 source
->GetResponseAsString(&data
);
222 // Only up to the first 16 characters of the response are important to GAIA.
223 // Truncate if needed to keep amount data sent back to GAIA down.
224 if (data
.size() > 16)
226 results_
[fetchers_
[url
].first
] = data
;
228 // Clean up tracking of this fetcher. The rest will be cleaned up after
229 // the timer expires in CleanupTransientState().
230 DCHECK_EQ(source
, fetchers_
[url
].second
);
231 fetchers_
.erase(url
);
234 // If all expected responses have been received, cancel the timer and
235 // report the result.
236 if (fetchers_
.empty()) {
237 CleanupTransientState();
238 GetCheckConnectionInfoCompleted(true);
243 void GaiaCookieManagerService::ExternalCcResultFetcher::Timeout() {
244 CleanupTransientState();
245 GetCheckConnectionInfoCompleted(false);
248 void GaiaCookieManagerService::ExternalCcResultFetcher::
249 CleanupTransientState() {
251 helper_
->gaia_auth_fetcher_
.reset();
253 for (URLToTokenAndFetcher::const_iterator it
= fetchers_
.begin();
254 it
!= fetchers_
.end(); ++it
) {
255 delete it
->second
.second
;
260 void GaiaCookieManagerService::ExternalCcResultFetcher::
261 GetCheckConnectionInfoCompleted(bool succeeded
) {
262 base::TimeDelta time_to_check_connections
=
263 base::Time::Now() - m_external_cc_result_start_time_
;
264 signin_metrics::LogExternalCcResultFetches(succeeded
,
265 time_to_check_connections
);
267 helper_
->external_cc_result_fetched_
= true;
268 // Since the ExternalCCResultFetcher is only Started in place of calling
269 // StartFetchingMergeSession, we can assume we need to call
270 // StartFetchingMergeSession. If this assumption becomes invalid, a Callback
271 // will need to be passed to Start() and Run() here.
272 helper_
->StartFetchingMergeSession();
275 GaiaCookieManagerService::GaiaCookieManagerService(
276 OAuth2TokenService
* token_service
,
277 const std::string
& source
,
278 SigninClient
* signin_client
)
279 : token_service_(token_service
),
280 signin_client_(signin_client
),
281 external_cc_result_fetcher_(this),
282 fetcher_backoff_(&kBackoffPolicy
),
285 external_cc_result_fetched_(false),
286 list_accounts_fetched_once_(false) {
289 GaiaCookieManagerService::~GaiaCookieManagerService() {
291 DCHECK(requests_
.empty());
294 void GaiaCookieManagerService::Init() {
295 cookie_changed_subscription_
= signin_client_
->AddCookieChangedCallback(
296 GaiaUrls::GetInstance()->google_url(),
298 base::Bind(&GaiaCookieManagerService::OnCookieChanged
,
299 base::Unretained(this)));
302 void GaiaCookieManagerService::Shutdown() {
303 cookie_changed_subscription_
.reset();
307 void GaiaCookieManagerService::AddAccountToCookieInternal(
308 const std::string
& account_id
) {
309 DCHECK(!account_id
.empty());
310 if (!signin_client_
->AreSigninCookiesAllowed()) {
311 SignalComplete(account_id
,
312 GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED
));
316 requests_
.push_back(GaiaCookieRequest::CreateAddAccountRequest(account_id
));
317 if (requests_
.size() == 1) {
318 signin_client_
->DelayNetworkCall(
319 base::Bind(&GaiaCookieManagerService::StartFetchingUbertoken
,
320 base::Unretained(this)));
324 void GaiaCookieManagerService::AddAccountToCookie(
325 const std::string
& account_id
) {
326 VLOG(1) << "GaiaCookieManagerService::AddAccountToCookie: " << account_id
;
327 access_token_
= std::string();
328 AddAccountToCookieInternal(account_id
);
331 void GaiaCookieManagerService::AddAccountToCookieWithToken(
332 const std::string
& account_id
,
333 const std::string
& access_token
) {
334 VLOG(1) << "GaiaCookieManagerService::AddAccountToCookieWithToken: "
336 DCHECK(!access_token
.empty());
337 access_token_
= access_token
;
338 AddAccountToCookieInternal(account_id
);
341 bool GaiaCookieManagerService::ListAccounts(
342 std::vector
<gaia::ListedAccount
>* accounts
) {
346 // There is a fetch currently executing (the results being provided in the
347 // parameter don't align with the fetches that have been started), or the list
348 // of accounts haven't been fetched even once.
349 if (!requests_
.empty())
352 if (!list_accounts_fetched_once_
) {
353 fetcher_retries_
= 0;
354 requests_
.push_back(GaiaCookieRequest::CreateListAccountsRequest());
355 signin_client_
->DelayNetworkCall(
356 base::Bind(&GaiaCookieManagerService::StartFetchingListAccounts
,
357 base::Unretained(this)));
361 accounts
->assign(listed_accounts_
.begin(), listed_accounts_
.end());
365 void GaiaCookieManagerService::LogOutAllAccounts() {
366 VLOG(1) << "GaiaCookieManagerService::LogOutAllAccounts";
368 bool log_out_queued
= false;
369 if (!requests_
.empty()) {
370 // Track requests to keep; all other unstarted requests will be removed.
371 std::vector
<GaiaCookieRequest
> requests_to_keep
;
373 // Check all pending, non-executing requests.
374 for (auto it
= requests_
.begin() + 1; it
!= requests_
.end(); ++it
) {
375 if (it
->request_type() == GaiaCookieRequestType::ADD_ACCOUNT
) {
376 // We have a pending log in request for an account followed by
378 GoogleServiceAuthError
error(GoogleServiceAuthError::REQUEST_CANCELED
);
379 SignalComplete(it
->account_id(), error
);
382 // Keep all requests except for ADD_ACCOUNTS.
383 if (it
->request_type() != GaiaCookieRequestType::ADD_ACCOUNT
)
384 requests_to_keep
.push_back(*it
);
386 // Verify a LOG_OUT isn't already queued.
387 if (it
->request_type() == GaiaCookieRequestType::LOG_OUT
)
388 log_out_queued
= true;
391 // Verify a LOG_OUT isn't currently being processed.
392 if (requests_
.front().request_type() == GaiaCookieRequestType::LOG_OUT
)
393 log_out_queued
= true;
395 // Remove all but the executing request. Re-add all requests being kept.
396 if (requests_
.size() > 1) {
397 requests_
.erase(requests_
.begin() + 1, requests_
.end());
399 requests_
.end(), requests_to_keep
.begin(), requests_to_keep
.end());
403 if (!log_out_queued
) {
404 requests_
.push_back(GaiaCookieRequest::CreateLogOutRequest());
405 if (requests_
.size() == 1) {
406 fetcher_retries_
= 0;
407 signin_client_
->DelayNetworkCall(
408 base::Bind(&GaiaCookieManagerService::StartFetchingLogOut
,
409 base::Unretained(this)));
414 void GaiaCookieManagerService::AddObserver(Observer
* observer
) {
415 observer_list_
.AddObserver(observer
);
418 void GaiaCookieManagerService::RemoveObserver(Observer
* observer
) {
419 observer_list_
.RemoveObserver(observer
);
422 void GaiaCookieManagerService::CancelAll() {
423 VLOG(1) << "GaiaCookieManagerService::CancelAll";
424 gaia_auth_fetcher_
.reset();
425 uber_token_fetcher_
.reset();
427 fetcher_timer_
.Stop();
430 void GaiaCookieManagerService::OnCookieChanged(
431 const net::CanonicalCookie
& cookie
,
433 DCHECK_EQ("APISID", cookie
.Name());
434 DCHECK_EQ(GaiaUrls::GetInstance()->google_url().host(), cookie
.Domain());
435 // Ignore changes to the cookie while requests are pending. These changes
436 // are caused by the service itself as it adds accounts. A side effects is
437 // that any changes to the gaia cookie outside of this class, while requests
438 // are pending, will be lost. However, trying to process these changes could
439 // cause an endless loop (see crbug.com/516070).
440 if (requests_
.empty()) {
441 requests_
.push_back(GaiaCookieRequest::CreateListAccountsRequest());
442 fetcher_retries_
= 0;
443 signin_client_
->DelayNetworkCall(
444 base::Bind(&GaiaCookieManagerService::StartFetchingListAccounts
,
445 base::Unretained(this)));
449 void GaiaCookieManagerService::SignalComplete(
450 const std::string
& account_id
,
451 const GoogleServiceAuthError
& error
) {
452 // Its possible for the observer to delete |this| object. Don't access
453 // access any members after this calling the observer. This method should
454 // be the last call in any other method.
455 FOR_EACH_OBSERVER(Observer
, observer_list_
,
456 OnAddAccountToCookieCompleted(account_id
, error
));
459 void GaiaCookieManagerService::OnUbertokenSuccess(
460 const std::string
& uber_token
) {
461 DCHECK(requests_
.front().request_type() ==
462 GaiaCookieRequestType::ADD_ACCOUNT
);
463 VLOG(1) << "GaiaCookieManagerService::OnUbertokenSuccess"
464 << " account=" << requests_
.front().account_id();
465 fetcher_retries_
= 0;
466 uber_token_
= uber_token
;
468 if (!external_cc_result_fetched_
&&
469 !external_cc_result_fetcher_
.IsRunning()) {
470 external_cc_result_fetcher_
.Start();
474 signin_client_
->DelayNetworkCall(
475 base::Bind(&GaiaCookieManagerService::StartFetchingMergeSession
,
476 base::Unretained(this)));
479 void GaiaCookieManagerService::OnUbertokenFailure(
480 const GoogleServiceAuthError
& error
) {
481 // Note that the UberToken fetcher already retries transient errors.
482 VLOG(1) << "Failed to retrieve ubertoken"
483 << " account=" << requests_
.front().account_id()
484 << " error=" << error
.ToString();
485 const std::string account_id
= requests_
.front().account_id();
487 SignalComplete(account_id
, error
);
490 void GaiaCookieManagerService::OnMergeSessionSuccess(const std::string
& data
) {
491 VLOG(1) << "MergeSession successful account="
492 << requests_
.front().account_id();
493 DCHECK(requests_
.front().request_type() ==
494 GaiaCookieRequestType::ADD_ACCOUNT
);
495 const std::string account_id
= requests_
.front().account_id();
497 SignalComplete(account_id
, GoogleServiceAuthError::AuthErrorNone());
499 fetcher_backoff_
.InformOfRequest(true);
500 uber_token_
= std::string();
503 void GaiaCookieManagerService::OnMergeSessionFailure(
504 const GoogleServiceAuthError
& error
) {
505 DCHECK(requests_
.front().request_type() ==
506 GaiaCookieRequestType::ADD_ACCOUNT
);
507 VLOG(1) << "Failed MergeSession"
508 << " account=" << requests_
.front().account_id()
509 << " error=" << error
.ToString();
510 if (++fetcher_retries_
< kMaxFetcherRetries
&& error
.IsTransientError()) {
511 fetcher_backoff_
.InformOfRequest(false);
512 UMA_HISTOGRAM_ENUMERATION("OAuth2Login.MergeSessionRetry",
513 error
.state(), GoogleServiceAuthError::NUM_STATES
);
514 fetcher_timer_
.Start(
515 FROM_HERE
, fetcher_backoff_
.GetTimeUntilRelease(),
516 base::Bind(&SigninClient::DelayNetworkCall
,
517 base::Unretained(signin_client_
),
519 &GaiaCookieManagerService::StartFetchingMergeSession
,
520 base::Unretained(this))));
524 uber_token_
= std::string();
525 const std::string account_id
= requests_
.front().account_id();
527 UMA_HISTOGRAM_ENUMERATION("OAuth2Login.MergeSessionFailure",
528 error
.state(), GoogleServiceAuthError::NUM_STATES
);
530 SignalComplete(account_id
, error
);
533 void GaiaCookieManagerService::OnListAccountsSuccess(const std::string
& data
) {
534 VLOG(1) << "ListAccounts successful";
535 DCHECK(requests_
.front().request_type() ==
536 GaiaCookieRequestType::LIST_ACCOUNTS
);
537 fetcher_backoff_
.InformOfRequest(true);
539 if (!gaia::ParseListAccountsData(data
, &listed_accounts_
)) {
540 listed_accounts_
.clear();
541 OnListAccountsFailure(GoogleServiceAuthError(
542 GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE
));
546 for (gaia::ListedAccount
& account
: listed_accounts_
) {
547 DCHECK(account
.id
.empty());
548 account
.id
= AccountTrackerService::PickAccountIdForAccount(
549 signin_client_
->GetPrefs(), account
.gaia_id
, account
.email
);
552 list_accounts_fetched_once_
= true;
554 // HandleNextRequest before sending out the notification because some
555 // services, in response to OnGaiaAccountsInCookieUpdated, may try in return
556 // to call ListAccounts, which would immediately return false if the
557 // ListAccounts request is still sitting in queue.
558 FOR_EACH_OBSERVER(Observer
, observer_list_
,
559 OnGaiaAccountsInCookieUpdated(
561 GoogleServiceAuthError(GoogleServiceAuthError::NONE
)));
564 void GaiaCookieManagerService::OnListAccountsFailure(
565 const GoogleServiceAuthError
& error
) {
566 VLOG(1) << "ListAccounts failed";
567 DCHECK(requests_
.front().request_type() ==
568 GaiaCookieRequestType::LIST_ACCOUNTS
);
569 if (++fetcher_retries_
< kMaxFetcherRetries
&& error
.IsTransientError()) {
570 fetcher_backoff_
.InformOfRequest(false);
571 UMA_HISTOGRAM_ENUMERATION("Signin.ListAccountsRetry",
572 error
.state(), GoogleServiceAuthError::NUM_STATES
);
573 fetcher_timer_
.Start(
574 FROM_HERE
, fetcher_backoff_
.GetTimeUntilRelease(),
575 base::Bind(&SigninClient::DelayNetworkCall
,
576 base::Unretained(signin_client_
),
578 &GaiaCookieManagerService::StartFetchingListAccounts
,
579 base::Unretained(this))));
583 UMA_HISTOGRAM_ENUMERATION("Signin.ListAccountsFailure",
584 error
.state(), GoogleServiceAuthError::NUM_STATES
);
585 FOR_EACH_OBSERVER(Observer
, observer_list_
,
586 OnGaiaAccountsInCookieUpdated(listed_accounts_
, error
));
590 void GaiaCookieManagerService::OnLogOutSuccess() {
591 DCHECK(requests_
.front().request_type() == GaiaCookieRequestType::LOG_OUT
);
592 VLOG(1) << "GaiaCookieManagerService::OnLogOutSuccess";
594 fetcher_backoff_
.InformOfRequest(true);
598 void GaiaCookieManagerService::OnLogOutFailure(
599 const GoogleServiceAuthError
& error
) {
600 DCHECK(requests_
.front().request_type() == GaiaCookieRequestType::LOG_OUT
);
601 VLOG(1) << "GaiaCookieManagerService::OnLogOutFailure";
603 if (++fetcher_retries_
< kMaxFetcherRetries
) {
604 fetcher_backoff_
.InformOfRequest(false);
605 fetcher_timer_
.Start(
606 FROM_HERE
, fetcher_backoff_
.GetTimeUntilRelease(),
607 base::Bind(&SigninClient::DelayNetworkCall
,
608 base::Unretained(signin_client_
),
609 base::Bind(&GaiaCookieManagerService::StartFetchingLogOut
,
610 base::Unretained(this))));
617 void GaiaCookieManagerService::StartFetchingUbertoken() {
618 VLOG(1) << "GaiaCookieManagerService::StartFetchingUbertoken account_id="
619 << requests_
.front().account_id();
620 uber_token_fetcher_
.reset(new UbertokenFetcher(
621 token_service_
, this, source_
, signin_client_
->GetURLRequestContext(),
622 base::Bind(&SigninClient::CreateGaiaAuthFetcher
,
623 base::Unretained(signin_client_
))));
624 if (access_token_
.empty()) {
625 uber_token_fetcher_
->StartFetchingToken(requests_
.front().account_id());
627 uber_token_fetcher_
->StartFetchingTokenWithAccessToken(
628 requests_
.front().account_id(), access_token_
);
632 void GaiaCookieManagerService::StartFetchingMergeSession() {
633 DCHECK(!uber_token_
.empty());
634 gaia_auth_fetcher_
.reset(signin_client_
->CreateGaiaAuthFetcher(
635 this, source_
, signin_client_
->GetURLRequestContext()));
637 gaia_auth_fetcher_
->StartMergeSession(uber_token_
,
638 external_cc_result_fetcher_
.GetExternalCcResult());
641 void GaiaCookieManagerService::StartFetchingLogOut() {
642 DCHECK(requests_
.front().request_type() == GaiaCookieRequestType::LOG_OUT
);
643 VLOG(1) << "GaiaCookieManagerService::StartFetchingLogOut";
644 gaia_auth_fetcher_
.reset(signin_client_
->CreateGaiaAuthFetcher(
645 this, source_
, signin_client_
->GetURLRequestContext()));
646 gaia_auth_fetcher_
->StartLogOut();
649 void GaiaCookieManagerService::StartFetchingListAccounts() {
650 VLOG(1) << "GaiaCookieManagerService::ListAccounts";
651 gaia_auth_fetcher_
.reset(signin_client_
->CreateGaiaAuthFetcher(
652 this, source_
, signin_client_
->GetURLRequestContext()));
653 gaia_auth_fetcher_
->StartListAccounts();
656 void GaiaCookieManagerService::HandleNextRequest() {
657 VLOG(1) << "GaiaCookieManagerService::HandleNextRequest";
658 if (requests_
.front().request_type() ==
659 GaiaCookieRequestType::LIST_ACCOUNTS
) {
660 // This and any directly subsequent list accounts would return the same.
661 while (!requests_
.empty() && requests_
.front().request_type() ==
662 GaiaCookieRequestType::LIST_ACCOUNTS
) {
663 requests_
.pop_front();
666 // Pop the completed request.
667 requests_
.pop_front();
670 gaia_auth_fetcher_
.reset();
671 fetcher_retries_
= 0;
672 if (requests_
.empty()) {
673 VLOG(1) << "GaiaCookieManagerService::HandleNextRequest: no more";
674 uber_token_fetcher_
.reset();
675 access_token_
= std::string();
677 switch (requests_
.front().request_type()) {
678 case GaiaCookieRequestType::ADD_ACCOUNT
:
679 signin_client_
->DelayNetworkCall(
680 base::Bind(&GaiaCookieManagerService::StartFetchingUbertoken
,
681 base::Unretained(this)));
683 case GaiaCookieRequestType::LOG_OUT
:
684 signin_client_
->DelayNetworkCall(
685 base::Bind(&GaiaCookieManagerService::StartFetchingLogOut
,
686 base::Unretained(this)));
688 case GaiaCookieRequestType::LIST_ACCOUNTS
:
689 uber_token_fetcher_
.reset();
690 signin_client_
->DelayNetworkCall(
691 base::Bind(&GaiaCookieManagerService::StartFetchingListAccounts
,
692 base::Unretained(this)));