blob: c048d305dbb3ec29db1df7ccf59450083a349c96 [file] [log] [blame]
// 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.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.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<ProfileOAuth2TokenServiceRequest::Core>,
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<ProfileOAuth2TokenServiceRequest::Core>;
// 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<base::SingleThreadTaskRunner> owner_task_runner_;
// OAuth2TokenService request for fetching OAuth2 access token; it should be
// created, reset and accessed only on the UI thread.
scoped_ptr<OAuth2TokenService::Request> request_;
DISALLOW_COPY_AND_ASSIGN(Core);
};
ProfileOAuth2TokenServiceRequest::Core::Core(
Profile* profile,
ProfileOAuth2TokenServiceRequest* owner)
: 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);
std::string account_id_to_use =
account_id.empty() ? service->GetPrimaryAccountId() : 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();
}