// Copyright 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/signin/profile_oauth2_token_service_request.h" #include "base/bind.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/signin_manager_factory.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" #include "components/signin/core/browser/signin_manager.h" #include "content/public/browser/browser_thread.h" #include "google_apis/gaia/google_service_auth_error.h" #include "google_apis/gaia/oauth2_access_token_consumer.h" class ProfileOAuth2TokenServiceRequest::Core : public base::RefCountedThreadSafe, public OAuth2TokenService::Consumer { public: // Note the thread where an instance of Core is constructed is referred to as // the "owner thread" here. This will be the thread of |owner_task_runner_|. Core(Profile* profile, ProfileOAuth2TokenServiceRequest* owner); // Starts fetching an OAuth2 access token for |account_id| and |scopes|. It // should be called on the owner thread. void Start( const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes); // Stops the OAuth2 access token fetching. It should be called on the owner // thread. void Stop(); OAuth2TokenService::Request* request(); // OAuth2TokenService::Consumer. It should be called on the UI thread. virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request, const std::string& access_token, const base::Time& expiration_time) OVERRIDE; virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request, const GoogleServiceAuthError& error) OVERRIDE; private: friend class base::RefCountedThreadSafe; // Note this can be destructed on the owner thread or on the UI thread, // depending on the reference count. virtual ~Core(); // Starts an OAuth2TokenService::Request on the UI thread. void StartOnUIThread( const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes); // Stops the OAuth2TokenService::Request on the UI thread. void StopOnUIThread(); // Method posted to the owner thread to call back |owner_|. Note when this // posted task is actually running on the owner thread, it is possible that // |owner_| has been reset NULL. void InformOwnerOnGetTokenSuccess(std::string access_token, base::Time expiration_time); void InformOwnerOnGetTokenFailure(GoogleServiceAuthError error); // The profile with which this instance was initialized. Profile* const profile_; // The object to call back when fetching completes. |owner_| should be // called back only on the owner thread. ProfileOAuth2TokenServiceRequest* owner_; // Task runner on which |owner_| should be called back. scoped_refptr owner_task_runner_; // OAuth2TokenService request for fetching OAuth2 access token; it should be // created, reset and accessed only on the UI thread. scoped_ptr request_; DISALLOW_COPY_AND_ASSIGN(Core); }; ProfileOAuth2TokenServiceRequest::Core::Core( Profile* profile, ProfileOAuth2TokenServiceRequest* owner) : OAuth2TokenService::Consumer("oauth2_token_service"), profile_(profile), owner_(owner), owner_task_runner_(base::ThreadTaskRunnerHandle::Get()) { DCHECK(profile); DCHECK(owner); } ProfileOAuth2TokenServiceRequest::Core::~Core() { } void ProfileOAuth2TokenServiceRequest::Core::Start( const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes) { DCHECK(owner_task_runner_->BelongsToCurrentThread()); if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { StartOnUIThread(account_id, scopes); } else { content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, base::Bind(&ProfileOAuth2TokenServiceRequest::Core::StartOnUIThread, this, account_id, scopes)); } } void ProfileOAuth2TokenServiceRequest::Core::Stop() { DCHECK(owner_task_runner_->BelongsToCurrentThread()); // Detaches |owner_| from this instance so |owner_| will be called back only // if |Stop()| has never been called. owner_ = NULL; if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { StopOnUIThread(); } else { content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, base::Bind(&ProfileOAuth2TokenServiceRequest::Core::StopOnUIThread, this)); } } OAuth2TokenService::Request* ProfileOAuth2TokenServiceRequest::Core::request() { return request_.get(); } void ProfileOAuth2TokenServiceRequest::Core::StopOnUIThread() { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); request_.reset(); } void ProfileOAuth2TokenServiceRequest::Core::StartOnUIThread( const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); ProfileOAuth2TokenService* service = ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); DCHECK(service); SigninManagerBase* signin_manager = SigninManagerFactory::GetForProfile(profile_); DCHECK(signin_manager); std::string account_id_to_use = account_id.empty() ? signin_manager->GetAuthenticatedAccountId() : account_id; request_.reset( service->StartRequest(account_id_to_use, scopes, this).release()); } void ProfileOAuth2TokenServiceRequest::Core::OnGetTokenSuccess( const OAuth2TokenService::Request* request, const std::string& access_token, const base::Time& expiration_time) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); DCHECK_EQ(request_.get(), request); owner_task_runner_->PostTask(FROM_HERE, base::Bind( &ProfileOAuth2TokenServiceRequest::Core::InformOwnerOnGetTokenSuccess, this, access_token, expiration_time)); request_.reset(); } void ProfileOAuth2TokenServiceRequest::Core::OnGetTokenFailure( const OAuth2TokenService::Request* request, const GoogleServiceAuthError& error) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); DCHECK_EQ(request_.get(), request); owner_task_runner_->PostTask(FROM_HERE, base::Bind( &ProfileOAuth2TokenServiceRequest::Core::InformOwnerOnGetTokenFailure, this, error)); request_.reset(); } void ProfileOAuth2TokenServiceRequest::Core::InformOwnerOnGetTokenSuccess( std::string access_token, base::Time expiration_time) { DCHECK(owner_task_runner_->BelongsToCurrentThread()); if (owner_) owner_->consumer_->OnGetTokenSuccess(owner_, access_token, expiration_time); } void ProfileOAuth2TokenServiceRequest::Core::InformOwnerOnGetTokenFailure( GoogleServiceAuthError error) { DCHECK(owner_task_runner_->BelongsToCurrentThread()); if (owner_) owner_->consumer_->OnGetTokenFailure(owner_, error); } // static ProfileOAuth2TokenServiceRequest* ProfileOAuth2TokenServiceRequest::CreateAndStart( Profile* profile, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes, OAuth2TokenService::Consumer* consumer) { return new ProfileOAuth2TokenServiceRequest(profile, account_id, scopes, consumer); } ProfileOAuth2TokenServiceRequest::ProfileOAuth2TokenServiceRequest( Profile* profile, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes, OAuth2TokenService::Consumer* consumer) : consumer_(consumer), core_(new Core(profile, this)) { core_->Start(account_id, scopes); } ProfileOAuth2TokenServiceRequest::~ProfileOAuth2TokenServiceRequest() { DCHECK(CalledOnValidThread()); core_->Stop(); } std::string ProfileOAuth2TokenServiceRequest::GetAccountId() const { return core_->request()->GetAccountId(); }