summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorchron@chromium.org <chron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-12 02:10:46 +0000
committerchron@chromium.org <chron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-12 02:10:46 +0000
commitab23dbedf7ff80bc6de6cb4da5d6d965f5525a4d (patch)
tree53ef4c5e622999b4185ee28f2e965c8ad9517325 /chrome
parent83a18417667c673f30ca30a59cbbec2dd54c82d9 (diff)
downloadchromium_src-ab23dbedf7ff80bc6de6cb4da5d6d965f5525a4d.zip
chromium_src-ab23dbedf7ff80bc6de6cb4da5d6d965f5525a4d.tar.gz
chromium_src-ab23dbedf7ff80bc6de6cb4da5d6d965f5525a4d.tar.bz2
Add token serialization to token_service.
The token service now supports serialization of tokens into the web database. The token service doesn't actually know the email of the user though, and it's assumed that whoever is using the token service does. Not sure whether that's a bad choice or not, open to suggestions. This CL adds a new DB table. Design comments / corrections are welcome. Testing in Chromium OS is in progress and not yet done. This CL will not be submitted prior to more Chromium OS testing. BUG=47092,47093 TEST=Unit tests included Review URL: http://codereview.chromium.org/3061025 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@55835 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/chromeos/login/login_utils.cc13
-rw-r--r--chrome/browser/net/gaia/token_service.cc176
-rw-r--r--chrome/browser/net/gaia/token_service.h123
-rw-r--r--chrome/browser/net/gaia/token_service_unittest.cc226
-rw-r--r--chrome/browser/password_manager/password_store_default_unittest.cc15
-rw-r--r--chrome/browser/password_manager/password_store_win_unittest.cc12
-rw-r--r--chrome/browser/password_manager/password_store_x_unittest.cc12
-rw-r--r--chrome/browser/sync/profile_sync_service_startup_unittest.cc4
-rw-r--r--chrome/browser/webdata/web_data_service.cc83
-rw-r--r--chrome/browser/webdata/web_data_service.h28
-rw-r--r--chrome/browser/webdata/web_database.cc76
-rw-r--r--chrome/browser/webdata/web_database.h21
-rw-r--r--chrome/browser/webdata/web_database_unittest.cc60
-rw-r--r--chrome/test/signaling_task.h28
-rw-r--r--chrome/test/testing_profile.cc9
-rw-r--r--chrome/test/testing_profile.h6
16 files changed, 793 insertions, 99 deletions
diff --git a/chrome/browser/chromeos/login/login_utils.cc b/chrome/browser/chromeos/login/login_utils.cc
index 33c212e..c3ef2f3 100644
--- a/chrome/browser/chromeos/login/login_utils.cc
+++ b/chrome/browser/chromeos/login/login_utils.cc
@@ -153,10 +153,15 @@ void LoginUtilsImpl::CompleteLogin(const std::string& username,
*(CommandLine::ForCurrentProcess()),
logging::DELETE_OLD_LOG_FILE);
- // Supply credentials for sync and others to use
- profile->GetTokenService()->Initialize(GaiaConstants::kChromeOSSource,
- profile->GetRequestContext(),
- credentials);
+ // Supply credentials for sync and others to use. Load tokens from disk.
+ TokenService* token_service = profile->GetTokenService();
+ token_service->Initialize(GaiaConstants::kChromeOSSource,
+ profile);
+ token_service->LoadTokensFromDB();
+ token_service->UpdateCredentials(credentials);
+ if (token_service->AreCredentialsValid()) {
+ token_service->StartFetchingTokens();
+ }
// Take the credentials passed in and try to exchange them for
// full-fledged Google authentication cookies. This is
diff --git a/chrome/browser/net/gaia/token_service.cc b/chrome/browser/net/gaia/token_service.cc
index 0a87dfe..eaa7a1ae 100644
--- a/chrome/browser/net/gaia/token_service.cc
+++ b/chrome/browser/net/gaia/token_service.cc
@@ -5,26 +5,86 @@
#include "chrome/browser/net/gaia/token_service.h"
#include "base/string_util.h"
+#include "chrome/browser/profile.h"
#include "chrome/common/net/gaia/gaia_authenticator2.h"
#include "chrome/common/net/gaia/gaia_constants.h"
#include "chrome/common/notification_service.h"
-void TokenService::Initialize(
- const char* const source,
- URLRequestContextGetter* getter,
- const GaiaAuthConsumer::ClientLoginResult& credentials) {
+// Unfortunately kNumServices must be defined in the .h.
+const char* TokenService::kServices[] = {GaiaConstants::kSyncService,
+ GaiaConstants::kTalkService};
+TokenService::TokenService()
+ : token_loading_query_(NULL) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+}
- credentials_ = credentials;
+TokenService::~TokenService() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ ResetCredentialsInMemory();
+}
+
+void TokenService::Initialize(const char* const source,
+ Profile* profile) {
+
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+
+ getter_ = profile->GetRequestContext();
+ // Since the user can create a bookmark in incognito, sync may be running.
+ // Thus we have to go for explicit access.
+ web_data_service_ = profile->GetWebDataService(Profile::EXPLICIT_ACCESS);
source_ = std::string(source);
- sync_token_fetcher_.reset(new GaiaAuthenticator2(this, source_, getter));
- talk_token_fetcher_.reset(new GaiaAuthenticator2(this, source_, getter));
}
-bool TokenService::AreCredentialsValid() const {
+void TokenService::ResetCredentialsInMemory() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+
+ // Terminate any running fetchers. Callbacks will not return.
+ for (int i = 0; i < kNumServices; i++) {
+ fetchers_[i].reset();
+ }
+
+ // Cancel pending loads. Callbacks will not return.
+ if (token_loading_query_) {
+ web_data_service_->CancelRequest(token_loading_query_);
+ token_loading_query_ = NULL;
+ }
+
+ token_map_.clear();
+ credentials_ = GaiaAuthConsumer::ClientLoginResult();
+}
+
+void TokenService::UpdateCredentials(
+ const GaiaAuthConsumer::ClientLoginResult& credentials) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ credentials_ = credentials;
+
+ // Cancels any currently running requests.
+ for (int i = 0; i < kNumServices; i++) {
+ fetchers_[i].reset(new GaiaAuthenticator2(this, source_, getter_));
+ }
+}
+
+void TokenService::LoadTokensFromDB() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ token_loading_query_ = web_data_service_->GetAllTokens(this);
+}
+
+void TokenService::SaveAuthTokenToDB(const std::string& service,
+ const std::string& auth_token) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ web_data_service_->SetTokenForService(service, auth_token);
+}
+
+void TokenService::EraseTokensFromDB() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ web_data_service_->RemoveAllTokens();
+}
+
+const bool TokenService::AreCredentialsValid() const {
return !credentials_.lsid.empty() && !credentials_.sid.empty();
}
-bool TokenService::HasLsid() const {
+const bool TokenService::HasLsid() const {
return !credentials_.lsid.empty();
}
@@ -33,18 +93,18 @@ const std::string& TokenService::GetLsid() const {
}
void TokenService::StartFetchingTokens() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
DCHECK(AreCredentialsValid());
- sync_token_fetcher_->StartIssueAuthToken(credentials_.sid,
- credentials_.lsid,
- GaiaConstants::kSyncService);
- talk_token_fetcher_->StartIssueAuthToken(credentials_.sid,
- credentials_.lsid,
- GaiaConstants::kTalkService);
+ for (int i = 0; i < kNumServices; i++) {
+ fetchers_[i]->StartIssueAuthToken(credentials_.sid,
+ credentials_.lsid,
+ kServices[i]);
+ }
}
// Services dependent on a token will check if a token is available.
// If it isn't, they'll go to sleep until they get a token event.
-bool TokenService::HasTokenForService(const char* const service) const {
+const bool TokenService::HasTokenForService(const char* const service) const {
return token_map_.count(service) > 0;
}
@@ -52,28 +112,96 @@ const std::string& TokenService::GetTokenForService(
const char* const service) const {
if (token_map_.count(service) > 0) {
- // map[key] is not const
+ // Note map[key] is not const.
return (*token_map_.find(service)).second;
}
return EmptyString();
}
-void TokenService::OnIssueAuthTokenSuccess(const std::string& service,
- const std::string& auth_token) {
- LOG(INFO) << "Got an authorization token for " << service;
- token_map_[service] = auth_token;
+// Note that this can fire twice or more for any given service.
+// It can fire once from the DB read, and then once from the initial
+// fetcher. Future fetches can cause more notification firings.
+// The DB read will not however fire a notification if the fetcher
+// returned first. So it's always safe to use the latest notification.
+void TokenService::FireTokenAvailableNotification(
+ const std::string& service,
+ const std::string& auth_token) {
+
TokenAvailableDetails details(service, auth_token);
NotificationService::current()->Notify(
NotificationType::TOKEN_AVAILABLE,
Source<TokenService>(this),
Details<const TokenAvailableDetails>(&details));
}
-void TokenService::OnIssueAuthTokenFailure(const std::string& service,
- const GaiaAuthError& error) {
- LOG(WARNING) << "Auth token issuing failed for service:" << service;
+
+void TokenService::FireTokenRequestFailedNotification(
+ const std::string& service,
+ const GaiaAuthError& error) {
+
TokenRequestFailedDetails details(service, error);
NotificationService::current()->Notify(
NotificationType::TOKEN_REQUEST_FAILED,
Source<TokenService>(this),
Details<const TokenRequestFailedDetails>(&details));
}
+
+void TokenService::OnIssueAuthTokenSuccess(const std::string& service,
+ const std::string& auth_token) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ LOG(INFO) << "Got an authorization token for " << service;
+ token_map_[service] = auth_token;
+ FireTokenAvailableNotification(service, auth_token);
+ SaveAuthTokenToDB(service, auth_token);
+}
+
+void TokenService::OnIssueAuthTokenFailure(const std::string& service,
+ const GaiaAuthError& error) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ LOG(WARNING) << "Auth token issuing failed for service:" << service;
+ FireTokenRequestFailedNotification(service, error);
+}
+
+void TokenService::OnWebDataServiceRequestDone(WebDataService::Handle h,
+ const WDTypedResult* result) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ DCHECK(token_loading_query_);
+ token_loading_query_ = NULL;
+
+ // If the fetch failed, there will be no result. In that case, we just don't
+ // load any tokens at all from the DB.
+ if (result) {
+ DCHECK(result->GetType() == TOKEN_RESULT);
+ const WDResult<std::map<std::string, std::string> > * token_result =
+ static_cast<const WDResult<std::map<std::string, std::string> > * > (
+ result);
+ LoadTokensIntoMemory(token_result->GetValue(), &token_map_);
+ }
+}
+
+// Load tokens from the db_token map into the in memory token map.
+void TokenService::LoadTokensIntoMemory(
+ const std::map<std::string, std::string>& db_tokens,
+ std::map<std::string, std::string>* in_memory_tokens) {
+
+ for (int i = 0; i < kNumServices; i++) {
+ // OnIssueAuthTokenSuccess should come from the same thread.
+ // If a token is already present in the map, it could only have
+ // come from a DB read or from IssueAuthToken. Since we should never
+ // fetch from the DB twice in a browser session, it must be from
+ // OnIssueAuthTokenSuccess, which is a live fetcher.
+ //
+ // Network fetched tokens take priority over DB tokens, so exclude tokens
+ // which have already been loaded by the fetcher.
+ if (!in_memory_tokens->count(kServices[i]) &&
+ db_tokens.count(kServices[i])) {
+ std::string db_token = db_tokens.find(kServices[i])->second;
+ if (!db_token.empty()) {
+ LOG(INFO) << "Loading " << kServices[i] << "token from DB:"
+ << db_token;
+ (*in_memory_tokens)[kServices[i]] = db_token;
+ FireTokenAvailableNotification(kServices[i], db_token);
+ // Failures are only for network errors.
+ }
+ }
+ }
+}
diff --git a/chrome/browser/net/gaia/token_service.h b/chrome/browser/net/gaia/token_service.h
index cf13699..42cb0ea 100644
--- a/chrome/browser/net/gaia/token_service.h
+++ b/chrome/browser/net/gaia/token_service.h
@@ -3,7 +3,33 @@
// found in the LICENSE file.
//
// The TokenService will supply authentication tokens for any service that
-// needs it, such as sync.
+// needs it, such as sync. Whenever the user logs in, a controller watching
+// the token service is expected to call ClientLogin to derive a new SID and
+// LSID. Whenever such credentials are available, the TokenService should be
+// updated with new credentials. The controller should then start fetching
+// tokens, which will be written to the database after retrieval, as well as
+// provided to listeners.
+//
+// A token service controller like the ChromiumOS login is expected to:
+//
+// Initialize() // Soon as you can
+// LoadTokensFromDB() // When it's OK to talk to the database
+// UpdateCredentials() // When user logs in
+// StartFetchingTokens() // When it's safe to start fetching
+//
+// Typically a user of the TokenService is expected just to call:
+//
+// if (token_service.HasTokenForService(servicename)) {
+// SetMyToken(token_service.GetTokenForService(servicename));
+// }
+// RegisterSomeObserver(token_service);
+//
+// Whenever a token update occurs:
+// OnTokenAvailable(...) {
+// if (IsServiceICareAbout(notification.service())) {
+// SetMyToken(notification.token())
+// }
+// }
#ifndef CHROME_BROWSER_NET_GAIA_TOKEN_SERVICE_H_
#define CHROME_BROWSER_NET_GAIA_TOKEN_SERVICE_H_
@@ -11,18 +37,22 @@
#include <map>
#include <string>
+#include <vector>
#include "base/scoped_ptr.h"
#include "chrome/common/net/gaia/gaia_auth_consumer.h"
#include "chrome/common/net/gaia/gaia_authenticator2.h"
#include "chrome/test/testing_profile.h"
+#include "base/gtest_prod_util.h"
class URLRequestContextGetter;
// The TokenService is a Profile member, so all calls are expected
// from the UI thread.
-class TokenService : public GaiaAuthConsumer {
+class TokenService : public GaiaAuthConsumer,
+ public WebDataServiceConsumer {
public:
- TokenService() {}
+ TokenService();
+ virtual ~TokenService();
// Notification classes
class TokenAvailableDetails {
@@ -52,45 +82,94 @@ class TokenService : public GaiaAuthConsumer {
};
// Initialize this token service with a request source
- // (usually from a GaiaAuthConsumer constant), a getter and
- // results from ClientLogin.
- void Initialize(
- const char* const source,
- URLRequestContextGetter* getter,
+ // (usually from a GaiaAuthConsumer constant), and the profile.
+ // Typically you'd then update the credentials.
+ void Initialize(const char* const source, Profile* profile);
+
+ // Update the credentials in the token service.
+ // Afterwards you can StartFetchingTokens.
+ void UpdateCredentials(
const GaiaAuthConsumer::ClientLoginResult& credentials);
+ // Terminate any running requests and reset the TokenService to a clean
+ // slate. Resets in memory structures. Does not modify the DB.
+ // When this is done, no tokens will be left in memory and no
+ // user credentials will be left. Useful if a user is logging out.
+ // Initialize doesn't need to be called again but UpdateCredentials does.
+ void ResetCredentialsInMemory();
+
+ // Async load all tokens for services we know of from the DB.
+ // You should do this at startup. Optionally you can do it again
+ // after you reset in memory credentials.
+ void LoadTokensFromDB();
+
+ // Clear all DB stored tokens for the current profile. Tokens may still be
+ // available in memory. If a DB load is pending it may still be serviced.
+ void EraseTokensFromDB();
+
// For legacy services with their own auth routines, they can just read
// the LSID out directly. Deprecated.
- bool HasLsid() const;
+ const bool HasLsid() const;
const std::string& GetLsid() const;
+ // Did we get a proper LSID?
+ const bool AreCredentialsValid() const;
- // On login, StartFetchingTokens() should be called to kick off token
- // fetching.
// Tokens will be fetched for all services(sync, talk) in the background.
- // Results come back via event channel. Services can also poll.
+ // Results come back via event channel. Services can also poll before events
+ // are issued.
void StartFetchingTokens();
- bool HasTokenForService(const char* const service) const;
+ const bool HasTokenForService(const char* const service) const;
const std::string& GetTokenForService(const char* const service) const;
- // Callbacks from the fetchers.
- void OnIssueAuthTokenSuccess(const std::string& service,
- const std::string& auth_token);
- void OnIssueAuthTokenFailure(const std::string& service,
- const GaiaAuthError& error);
+ // GaiaAuthConsumer implementation.
+ virtual void OnIssueAuthTokenSuccess(const std::string& service,
+ const std::string& auth_token);
+ virtual void OnIssueAuthTokenFailure(const std::string& service,
+ const GaiaAuthError& error);
+
+ // WebDataServiceConsumer implementation.
+ virtual void OnWebDataServiceRequestDone(WebDataService::Handle h,
+ const WDTypedResult* result);
private:
- // Did we get a proper LSID?
- bool AreCredentialsValid() const;
+
+ void FireTokenAvailableNotification(const std::string& service,
+ const std::string& auth_token);
+
+ void FireTokenRequestFailedNotification(const std::string& service,
+ const GaiaAuthError& error);
+
+ void LoadTokensIntoMemory(const std::map<std::string, std::string>& in_toks,
+ std::map<std::string, std::string>* out_toks);
+
+ void SaveAuthTokenToDB(const std::string& service,
+ const std::string& auth_token);
+
+ // Web data service to access tokens from.
+ scoped_refptr<WebDataService> web_data_service_;
+ // Getter to use for fetchers.
+ scoped_refptr<URLRequestContextGetter> getter_;
+ // Request handle to load Gaia tokens from DB.
+ WebDataService::Handle token_loading_query_;
// Gaia request source for Gaia accounting.
std::string source_;
// Credentials from ClientLogin for Issuing auth tokens.
GaiaAuthConsumer::ClientLoginResult credentials_;
- scoped_ptr<GaiaAuthenticator2> sync_token_fetcher_;
- scoped_ptr<GaiaAuthenticator2> talk_token_fetcher_;
+
+ // Size of array of services (must be defined here).
+ static const int kNumServices = 2;
+ // List of services that we're performing operations for.
+ static const char* kServices[kNumServices];
+ // A bunch of fetchers suitable for token issuing. We don't care about
+ // the ordering, nor do we care which is for which service.
+ scoped_ptr<GaiaAuthenticator2> fetchers_[kNumServices];
// Map from service to token.
std::map<std::string, std::string> token_map_;
+ FRIEND_TEST_ALL_PREFIXES(TokenServiceTest, LoadTokensIntoMemoryBasic);
+ FRIEND_TEST_ALL_PREFIXES(TokenServiceTest, LoadTokensIntoMemoryAdvanced);
+
DISALLOW_COPY_AND_ASSIGN(TokenService);
};
diff --git a/chrome/browser/net/gaia/token_service_unittest.cc b/chrome/browser/net/gaia/token_service_unittest.cc
index 0bd7e56..a9c6863 100644
--- a/chrome/browser/net/gaia/token_service_unittest.cc
+++ b/chrome/browser/net/gaia/token_service_unittest.cc
@@ -4,12 +4,17 @@
//
// This file defines a unit test for the profile's token service.
+#include "base/scoped_temp_dir.h"
#include "chrome/browser/net/gaia/token_service.h"
-#include "chrome/common/net/gaia/gaia_authenticator2_unittest.h"
+#include "chrome/browser/password_manager/encryptor.h"
+#include "chrome/browser/webdata/web_data_service.h"
#include "chrome/common/net/gaia/gaia_auth_consumer.h"
+#include "chrome/common/net/gaia/gaia_authenticator2_unittest.h"
#include "chrome/common/net/gaia/gaia_constants.h"
-#include "testing/gtest/include/gtest/gtest.h"
+#include "chrome/common/net/test_url_fetcher_factory.h"
+#include "chrome/test/signaling_task.h"
#include "chrome/test/test_notification_tracker.h"
+#include "testing/gtest/include/gtest/gtest.h"
// TestNotificationTracker doesn't do a deep copy on the notification details.
// We have to in order to read it out, or we have a bad ptr, since the details
@@ -56,27 +61,73 @@ class TokenFailedTracker : public TestNotificationTracker {
class TokenServiceTest : public testing::Test {
public:
- TokenServiceTest() {
+ TokenServiceTest()
+ : ui_thread_(ChromeThread::UI, &message_loop_),
+ db_thread_(ChromeThread::DB) {
+ }
+
+ virtual void SetUp() {
+#if defined(OS_MACOSX)
+ Encryptor::UseMockKeychain(true);
+#endif
credentials_.sid = "sid";
credentials_.lsid = "lsid";
credentials_.token = "token";
credentials_.data = "data";
+ ASSERT_TRUE(temp_.CreateUniqueTempDir());
+ ASSERT_TRUE(db_thread_.Start());
+
+ // Testing profile responsible for deleting the temp dir.
+ profile_.reset(new TestingProfile(temp_.Take()));
+ profile_->CreateWebDataService(false);
+ WaitForDBLoadCompletion();
+
success_tracker_.ListenFor(NotificationType::TOKEN_AVAILABLE,
Source<TokenService>(&service_));
failure_tracker_.ListenFor(NotificationType::TOKEN_REQUEST_FAILED,
Source<TokenService>(&service_));
- service_.Initialize("test", profile_.GetRequestContext(), credentials_);
+ service_.Initialize("test", profile_.get());
+ service_.UpdateCredentials(credentials_);
+
+ URLFetcher::set_factory(NULL);
+ }
+
+ virtual void TearDown() {
+ // You have to destroy the profile before the db_thread_ stops.
+ if (profile_.get()) {
+ profile_.reset(NULL);
+ }
+
+ db_thread_.Stop();
+ MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask);
+ MessageLoop::current()->Run();
}
+ void WaitForDBLoadCompletion() {
+ // The WebDB does all work on the DB thread. This will add an event
+ // to the end of the DB thread, so when we reach this task, all DB
+ // operations should be complete.
+ WaitableEvent done(false, false);
+ ChromeThread::PostTask(
+ ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+ done.Wait();
+
+ // Notifications should be returned from the DB thread onto the UI thread.
+ message_loop_.RunAllPending();
+ }
+
+ MessageLoopForUI message_loop_;
+ ChromeThread ui_thread_; // Mostly so DCHECKS pass.
+ ChromeThread db_thread_; // WDS on here
+
TokenService service_;
TokenAvailableTracker success_tracker_;
TokenFailedTracker failure_tracker_;
GaiaAuthConsumer::ClientLoginResult credentials_;
-
- private:
- TestingProfile profile_;
+ scoped_ptr<TestingProfile> profile_;
+ ScopedTempDir temp_;
};
TEST_F(TokenServiceTest, SanityCheck) {
@@ -99,7 +150,7 @@ TEST_F(TokenServiceTest, NotificationSuccess) {
TokenService::TokenAvailableDetails details =
success_tracker_.get_last_token_details();
- // MSVC doesn't like this comparison as EQ
+ // MSVC doesn't like this comparison as EQ.
EXPECT_TRUE(details.service() == GaiaConstants::kSyncService);
EXPECT_EQ(details.token(), "token");
}
@@ -116,9 +167,9 @@ TEST_F(TokenServiceTest, NotificationFailed) {
TokenService::TokenRequestFailedDetails details =
failure_tracker_.get_last_token_details();
- // MSVC doesn't like this comparison as EQ
+ // MSVC doesn't like this comparison as EQ.
EXPECT_TRUE(details.service() == GaiaConstants::kSyncService);
- EXPECT_TRUE(details.error() == error); // Struct has no print function
+ EXPECT_TRUE(details.error() == error); // Struct has no print function.
}
TEST_F(TokenServiceTest, OnTokenSuccessUpdate) {
@@ -144,15 +195,54 @@ TEST_F(TokenServiceTest, OnTokenSuccess) {
// result with ClientLogin, it doesn't matter, we should still get it back.
EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), "token");
- // Check 2nd service
+ // Check the second service.
service_.OnIssueAuthTokenSuccess(GaiaConstants::kTalkService, "token2");
EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kTalkService));
EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kTalkService), "token2");
- // Didn't change
+ // It didn't change.
EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), "token");
}
+TEST_F(TokenServiceTest, ResetSimple) {
+ service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token");
+ EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
+ EXPECT_TRUE(service_.HasLsid());
+
+ service_.ResetCredentialsInMemory();
+
+ EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kSyncService));
+ EXPECT_FALSE(service_.HasLsid());
+}
+
+TEST_F(TokenServiceTest, ResetComplex) {
+ TestURLFetcherFactory factory;
+ URLFetcher::set_factory(&factory);
+ service_.StartFetchingTokens();
+ // You have to call delegates by hand with the test fetcher,
+ // Let's pretend only one returned.
+
+ service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "eraseme");
+ EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
+ EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService),
+ "eraseme");
+ EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kTalkService));
+
+ service_.ResetCredentialsInMemory();
+ EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kSyncService));
+ EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kTalkService));
+
+ // Now start using it again.
+ service_.UpdateCredentials(credentials_);
+ service_.StartFetchingTokens();
+
+ service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token");
+ service_.OnIssueAuthTokenSuccess(GaiaConstants::kTalkService, "token2");
+
+ EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), "token");
+ EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kTalkService), "token2");
+}
+
TEST_F(TokenServiceTest, FullIntegration) {
MockFactory<MockFetcher> factory;
std::string result = "SID=sid\nLSID=lsid\nAuth=auth\n";
@@ -169,4 +259,116 @@ TEST_F(TokenServiceTest, FullIntegration) {
// result with ClientLogin, it doesn't matter, we should still get it back.
EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), result);
EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kTalkService), result);
+
+ service_.ResetCredentialsInMemory();
+ EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kSyncService));
+ EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kTalkService));
+}
+
+TEST_F(TokenServiceTest, LoadTokensIntoMemoryBasic) {
+ // Validate that the method sets proper data in notifications and map.
+ std::map<std::string, std::string> db_tokens;
+ std::map<std::string, std::string> memory_tokens;
+
+ service_.LoadTokensIntoMemory(db_tokens, &memory_tokens);
+ EXPECT_TRUE(db_tokens.empty());
+ EXPECT_TRUE(memory_tokens.empty());
+ EXPECT_EQ(0U, success_tracker_.size());
+
+ db_tokens[GaiaConstants::kSyncService] = "token";
+ service_.LoadTokensIntoMemory(db_tokens, &memory_tokens);
+ EXPECT_EQ(1U, success_tracker_.size());
+
+ TokenService::TokenAvailableDetails details =
+ success_tracker_.get_last_token_details();
+ // MSVC doesn't like this comparison as EQ.
+ EXPECT_TRUE(details.service() == GaiaConstants::kSyncService);
+ EXPECT_EQ(details.token(), "token");
+ EXPECT_EQ(1U, memory_tokens.count(GaiaConstants::kSyncService));
+ EXPECT_EQ(memory_tokens[GaiaConstants::kSyncService], "token");
+}
+
+TEST_F(TokenServiceTest, LoadTokensIntoMemoryAdvanced) {
+ // LoadTokensIntoMemory should avoid setting tokens already in the
+ // token map.
+ std::map<std::string, std::string> db_tokens;
+ std::map<std::string, std::string> memory_tokens;
+
+ db_tokens["ignore"] = "token";
+
+ service_.LoadTokensIntoMemory(db_tokens, &memory_tokens);
+ EXPECT_TRUE(memory_tokens.empty());
+ db_tokens[GaiaConstants::kSyncService] = "pepper";
+
+ service_.LoadTokensIntoMemory(db_tokens, &memory_tokens);
+ EXPECT_EQ(1U, memory_tokens.count(GaiaConstants::kSyncService));
+ EXPECT_EQ(memory_tokens[GaiaConstants::kSyncService], "pepper");
+ EXPECT_EQ(1U, success_tracker_.size());
+ success_tracker_.Reset();
+
+ // SyncService token is already in memory. Pretend we got it off
+ // the disk as well, but an older token.
+ db_tokens[GaiaConstants::kSyncService] = "ignoreme";
+ db_tokens[GaiaConstants::kTalkService] = "tomato";
+ service_.LoadTokensIntoMemory(db_tokens, &memory_tokens);
+
+ EXPECT_EQ(2U, memory_tokens.size());
+ EXPECT_EQ(1U, memory_tokens.count(GaiaConstants::kTalkService));
+ EXPECT_EQ(memory_tokens[GaiaConstants::kTalkService], "tomato");
+ EXPECT_EQ(1U, success_tracker_.size());
+ EXPECT_EQ(1U, memory_tokens.count(GaiaConstants::kSyncService));
+ EXPECT_EQ(memory_tokens[GaiaConstants::kSyncService], "pepper");
+}
+
+TEST_F(TokenServiceTest, WebDBLoadIntegration) {
+ service_.LoadTokensFromDB();
+ WaitForDBLoadCompletion();
+ EXPECT_EQ(0U, success_tracker_.size());
+
+ // Should result in DB write.
+ service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token");
+ EXPECT_EQ(1U, success_tracker_.size());
+
+ EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
+ // Clean slate.
+ service_.ResetCredentialsInMemory();
+ success_tracker_.Reset();
+ EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kSyncService));
+
+ service_.LoadTokensFromDB();
+ WaitForDBLoadCompletion();
+
+ EXPECT_EQ(1U, success_tracker_.size());
+ EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
+ EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kTalkService));
+ EXPECT_FALSE(service_.HasLsid());
+}
+
+TEST_F(TokenServiceTest, MultipleLoadResetIntegration) {
+ // Should result in DB write.
+ service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token");
+ service_.ResetCredentialsInMemory();
+ success_tracker_.Reset();
+ EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kSyncService));
+
+ service_.LoadTokensFromDB();
+ WaitForDBLoadCompletion();
+
+ service_.LoadTokensFromDB(); // Should do nothing.
+ WaitForDBLoadCompletion();
+
+ EXPECT_EQ(1U, success_tracker_.size());
+ EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
+ EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kTalkService));
+ EXPECT_FALSE(service_.HasLsid());
+
+ // Reset it one more time so there's no surprises.
+ service_.ResetCredentialsInMemory();
+ success_tracker_.Reset();
+
+ service_.LoadTokensFromDB();
+ WaitForDBLoadCompletion();
+
+ EXPECT_EQ(1U, success_tracker_.size());
+ EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
}
diff --git a/chrome/browser/password_manager/password_store_default_unittest.cc b/chrome/browser/password_manager/password_store_default_unittest.cc
index f5629a4..7b4ef0b 100644
--- a/chrome/browser/password_manager/password_store_default_unittest.cc
+++ b/chrome/browser/password_manager/password_store_default_unittest.cc
@@ -14,6 +14,7 @@
#include "chrome/browser/webdata/web_data_service.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"
+#include "chrome/test/signaling_task.h"
#include "chrome/test/testing_profile.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -41,17 +42,6 @@ class MockWebDataServiceConsumer : public WebDataServiceConsumer {
const WDTypedResult*));
};
-class SignalingTask : public Task {
- public:
- explicit SignalingTask(WaitableEvent* event) : event_(event) {
- }
- virtual void Run() {
- event_->Signal();
- }
- private:
- WaitableEvent* event_;
-};
-
class MockNotificationObserver : public NotificationObserver {
public:
MOCK_METHOD3(Observe, void(NotificationType,
@@ -243,7 +233,8 @@ TEST_F(PasswordStoreDefaultTest, Migration) {
// Initializing the PasswordStore should trigger a migration.
scoped_refptr<PasswordStoreDefault> store(
- new PasswordStoreDefault(login_db_.release(), profile_.get(), wds_.get()));
+ new PasswordStoreDefault(login_db_.release(),
+ profile_.get(), wds_.get()));
store->Init();
// Check that the migration preference has not been initialized;
diff --git a/chrome/browser/password_manager/password_store_win_unittest.cc b/chrome/browser/password_manager/password_store_win_unittest.cc
index 987c95e..6bdea02 100644
--- a/chrome/browser/password_manager/password_store_win_unittest.cc
+++ b/chrome/browser/password_manager/password_store_win_unittest.cc
@@ -17,6 +17,7 @@
#include "chrome/browser/password_manager/password_store_win.h"
#include "chrome/browser/password_manager/ie7_password.h"
#include "chrome/common/pref_names.h"
+#include "chrome/test/signaling_task.h"
#include "chrome/test/testing_profile.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -41,17 +42,6 @@ public:
void(WebDataService::Handle, const WDTypedResult*));
};
-class SignalingTask : public Task {
- public:
- explicit SignalingTask(WaitableEvent* event) : event_(event) {
- }
- virtual void Run() {
- event_->Signal();
- }
- private:
- WaitableEvent* event_;
-};
-
} // anonymous namespace
typedef std::vector<PasswordForm*> VectorOfForms;
diff --git a/chrome/browser/password_manager/password_store_x_unittest.cc b/chrome/browser/password_manager/password_store_x_unittest.cc
index a172503..b8f56db 100644
--- a/chrome/browser/password_manager/password_store_x_unittest.cc
+++ b/chrome/browser/password_manager/password_store_x_unittest.cc
@@ -14,6 +14,7 @@
#include "chrome/browser/webdata/web_data_service.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"
+#include "chrome/test/signaling_task.h"
#include "chrome/test/testing_profile.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -43,17 +44,6 @@ class MockWebDataServiceConsumer : public WebDataServiceConsumer {
const WDTypedResult*));
};
-class SignalingTask : public Task {
- public:
- explicit SignalingTask(WaitableEvent* event) : event_(event) {
- }
- virtual void Run() {
- event_->Signal();
- }
- private:
- WaitableEvent* event_;
-};
-
class MockNotificationObserver : public NotificationObserver {
public:
MOCK_METHOD3(Observe, void(NotificationType,
diff --git a/chrome/browser/sync/profile_sync_service_startup_unittest.cc b/chrome/browser/sync/profile_sync_service_startup_unittest.cc
index f49354d..9d915c2 100644
--- a/chrome/browser/sync/profile_sync_service_startup_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_startup_unittest.cc
@@ -204,8 +204,8 @@ TEST_F(ProfileSyncServiceStartupBootstrapTest, SKIP_MACOSX(StartFirstTime)) {
result.sid = "sid";
result.lsid = "lsid";
profile_.GetTokenService()->Initialize("test",
- profile_.GetRequestContext(),
- result);
+ &profile_);
+ profile_.GetTokenService()->UpdateCredentials(result);
// Will start sync even though setup hasn't been completed (since
// setup is bypassed when bootstrapping is enabled).
diff --git a/chrome/browser/webdata/web_data_service.cc b/chrome/browser/webdata/web_data_service.cc
index 4b7f93e..384128d 100644
--- a/chrome/browser/webdata/web_data_service.cc
+++ b/chrome/browser/webdata/web_data_service.cc
@@ -190,6 +190,46 @@ WebDataService::Handle WebDataService::GetWebAppImages(
////////////////////////////////////////////////////////////////////////////////
//
+// Token Service
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void WebDataService::SetTokenForService(const std::string& service,
+ const std::string& token) {
+ GenericRequest2<std::string, std::string>* request =
+ new GenericRequest2<std::string, std::string>(
+ this, GetNextRequestHandle(), NULL, service, token);
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this, &WebDataService::SetTokenForServiceImpl,
+ request));
+}
+
+void WebDataService::RemoveAllTokens() {
+ GenericRequest<std::string>* request =
+ new GenericRequest<std::string>(
+ this, GetNextRequestHandle(), NULL, std::string());
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this,
+ &WebDataService::RemoveAllTokensImpl,
+ request));
+}
+
+// Null on failure. Success is WDResult<std::string>
+WebDataService::Handle WebDataService::GetAllTokens(
+ WebDataServiceConsumer* consumer) {
+
+ GenericRequest<std::string>* request =
+ new GenericRequest<std::string>(
+ this, GetNextRequestHandle(), consumer, std::string());
+ RegisterRequest(request);
+ ScheduleTask(NewRunnableMethod(this,
+ &WebDataService::GetAllTokensImpl,
+ request));
+ return request->GetHandle();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
// Password manager.
//
////////////////////////////////////////////////////////////////////////////////
@@ -687,6 +727,49 @@ void WebDataService::GetWebAppImagesImpl(GenericRequest<GURL>* request) {
////////////////////////////////////////////////////////////////////////////////
//
+// Token Service implementation.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// argument std::string is unused
+void WebDataService::RemoveAllTokensImpl(
+ GenericRequest<std::string>* request) {
+ InitializeDatabaseIfNecessary();
+ if (db_ && !request->IsCancelled()) {
+ if (db_->RemoveAllTokens()) {
+ ScheduleCommit();
+ }
+ }
+ request->RequestComplete();
+}
+
+void WebDataService::SetTokenForServiceImpl(
+ GenericRequest2<std::string, std::string>* request) {
+ InitializeDatabaseIfNecessary();
+ if (db_ && !request->IsCancelled()) {
+ if (db_->SetTokenForService(request->GetArgument1(),
+ request->GetArgument2())) {
+ ScheduleCommit();
+ }
+ }
+ request->RequestComplete();
+}
+
+// argument is unused
+void WebDataService::GetAllTokensImpl(
+ GenericRequest<std::string>* request) {
+ InitializeDatabaseIfNecessary();
+ if (db_ && !request->IsCancelled()) {
+ std::map<std::string, std::string> map;
+ db_->GetAllTokens(&map);
+ request->SetResult(
+ new WDResult<std::map<std::string, std::string> >(TOKEN_RESULT, map));
+ }
+ request->RequestComplete();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
// Password manager implementation.
//
////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/webdata/web_data_service.h b/chrome/browser/webdata/web_data_service.h
index 63cd3b29..120fdc4 100644
--- a/chrome/browser/webdata/web_data_service.h
+++ b/chrome/browser/webdata/web_data_service.h
@@ -64,6 +64,7 @@ typedef enum {
PASSWORD_IE7_RESULT, // WDResult<IE7PasswordInfo>
#endif
WEB_APP_IMAGES, // WDResult<WDAppImagesResult>
+ TOKEN_RESULT, // WDResult<std::vector<std::string>>
AUTOFILL_VALUE_RESULT, // WDResult<std::vector<string16>>
AUTOFILL_CHANGES, // WDResult<std::vector<AutofillChange>>
AUTOFILL_PROFILE_RESULT, // WDResult<AutoFillProfile>
@@ -331,6 +332,22 @@ class WebDataService
//////////////////////////////////////////////////////////////////////////////
//
+ // Token Service
+ //
+ //////////////////////////////////////////////////////////////////////////////
+
+ // Set a token to use for a specified service.
+ void SetTokenForService(const std::string& service,
+ const std::string& token);
+
+ // Remove all tokens stored in the web database.
+ void RemoveAllTokens();
+
+ // Null on failure. Success is WDResult<std::vector<std::string> >
+ Handle GetAllTokens(WebDataServiceConsumer* consumer);
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
// Password manager
// NOTE: These methods are all deprecated; new clients should use
// PasswordStore. These are only still here because Windows is (temporarily)
@@ -529,6 +546,17 @@ class WebDataService
//////////////////////////////////////////////////////////////////////////////
//
+ // Token Service.
+ //
+ //////////////////////////////////////////////////////////////////////////////
+
+ void RemoveAllTokensImpl(GenericRequest<std::string>* request);
+ void SetTokenForServiceImpl(
+ GenericRequest2<std::string, std::string>* request);
+ void GetAllTokensImpl(GenericRequest<std::string>* request);
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
// Password manager.
//
//////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/webdata/web_database.cc b/chrome/browser/webdata/web_database.cc
index 948dc8c..005c8b2 100644
--- a/chrome/browser/webdata/web_database.cc
+++ b/chrome/browser/webdata/web_database.cc
@@ -444,7 +444,7 @@ sql::InitStatus WebDatabase::Init(const FilePath& db_name) {
if (!InitKeywordsTable() || !InitLoginsTable() || !InitWebAppIconsTable() ||
!InitWebAppsTable() || !InitAutofillTable() ||
!InitAutofillDatesTable() || !InitAutoFillProfilesTable() ||
- !InitCreditCardsTable()) {
+ !InitCreditCardsTable() || !InitTokenServiceTable()) {
LOG(WARNING) << "Unable to initialize the web database.";
return sql::INIT_FAILURE;
}
@@ -545,6 +545,68 @@ bool WebDatabase::RemoveWebApp(const GURL& url) {
return delete_s2.Run();
}
+bool WebDatabase::RemoveAllTokens() {
+ sql::Statement s(db_.GetUniqueStatement(
+ "DELETE FROM token_service"));
+ if (!s) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+
+ return s.Run();
+}
+
+bool WebDatabase::SetTokenForService(const std::string& service,
+ const std::string& token) {
+ // Don't bother with a cached statement since this will be a relatively
+ // infrequent operation.
+ sql::Statement s(db_.GetUniqueStatement(
+ "INSERT OR REPLACE INTO token_service "
+ "(service, encrypted_token) VALUES (?, ?)"));
+ if (!s) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+
+ std::string encrypted_token;
+
+ bool encrypted = Encryptor::EncryptString(token, &encrypted_token);
+ if (!encrypted) {
+ return false;
+ }
+
+ s.BindString(0, service);
+ s.BindBlob(1, encrypted_token.data(),
+ static_cast<int>(encrypted_token.length()));
+ return s.Run();
+}
+
+bool WebDatabase::GetAllTokens(std::map<std::string, std::string>* tokens) {
+ sql::Statement s(db_.GetUniqueStatement(
+ "SELECT service, encrypted_token FROM token_service"));
+ if (!s) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+
+ while (s.Step()) {
+ std::string encrypted_token;
+ std::string decrypted_token;
+ std::string service;
+ service = s.ColumnString(0);
+ bool entry_ok = !service.empty() &&
+ s.ColumnBlobAsString(1, &encrypted_token);
+ if (entry_ok) {
+ Encryptor::DecryptString(encrypted_token, &decrypted_token);
+ (*tokens)[service] = decrypted_token;
+ } else {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
bool WebDatabase::InitKeywordsTable() {
if (!db_.DoesTableExist("keywords")) {
if (!db_.Execute("CREATE TABLE keywords ("
@@ -748,6 +810,18 @@ bool WebDatabase::InitWebAppsTable() {
return true;
}
+bool WebDatabase::InitTokenServiceTable() {
+ if (!db_.DoesTableExist("token_service")) {
+ if (!db_.Execute("CREATE TABLE token_service ("
+ "service VARCHAR PRIMARY KEY NOT NULL,"
+ "encrypted_token BLOB)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
bool WebDatabase::AddKeyword(const TemplateURL& url) {
DCHECK(url.id());
sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
diff --git a/chrome/browser/webdata/web_database.h b/chrome/browser/webdata/web_database.h
index ca24cf9..1a8d83b8 100644
--- a/chrome/browser/webdata/web_database.h
+++ b/chrome/browser/webdata/web_database.h
@@ -273,6 +273,26 @@ class WebDatabase {
bool RemoveWebApp(const GURL& url);
+ //////////////////////////////////////////////////////////////////////////////
+ //
+ // Token Service
+ //
+ //////////////////////////////////////////////////////////////////////////////
+
+ // Remove all tokens previously set with SetTokenForService.
+ bool RemoveAllTokens();
+
+ // Retrives all tokens previously set with SetTokenForService.
+ // Returns true if there were tokens and we decrypted them,
+ // false if there was a failure somehow
+ bool GetAllTokens(std::map<std::string, std::string>* tokens);
+
+ // Store a token in the token_service table. Stored encrypted. May cause
+ // a mac keychain popup.
+ // True if we encrypted a token and stored it, false otherwise.
+ bool SetTokenForService(const std::string& service,
+ const std::string& token);
+
private:
FRIEND_TEST_ALL_PREFIXES(WebDatabaseTest, Autofill);
FRIEND_TEST_ALL_PREFIXES(WebDatabaseTest, Autofill_AddChanges);
@@ -310,6 +330,7 @@ class WebDatabase {
bool InitAutofillDatesTable();
bool InitAutoFillProfilesTable();
bool InitCreditCardsTable();
+ bool InitTokenServiceTable();
bool InitWebAppIconsTable();
bool InitWebAppsTable();
diff --git a/chrome/browser/webdata/web_database_unittest.cc b/chrome/browser/webdata/web_database_unittest.cc
index 566d042..25109a7 100644
--- a/chrome/browser/webdata/web_database_unittest.cc
+++ b/chrome/browser/webdata/web_database_unittest.cc
@@ -1148,6 +1148,66 @@ TEST_F(WebDatabaseTest, WebAppImages) {
}
}
+TEST_F(WebDatabaseTest, TokenServiceGetAllRemoveAll) {
+ WebDatabase db;
+ ASSERT_EQ(sql::INIT_OK, db.Init(file_));
+
+ std::map<std::string, std::string> out_map;
+ std::string service;
+ std::string service2;
+ service = "testservice";
+ service2 = "othertestservice";
+
+ EXPECT_TRUE(db.GetAllTokens(&out_map));
+ EXPECT_TRUE(out_map.empty());
+
+ // Check that get all tokens works
+ EXPECT_TRUE(db.SetTokenForService(service, "pepperoni"));
+ EXPECT_TRUE(db.SetTokenForService(service2, "steak"));
+ EXPECT_TRUE(db.GetAllTokens(&out_map));
+ EXPECT_EQ(out_map.find(service)->second, "pepperoni");
+ EXPECT_EQ(out_map.find(service2)->second, "steak");
+ out_map.clear();
+
+ // Purge
+ EXPECT_TRUE(db.RemoveAllTokens());
+ EXPECT_TRUE(db.GetAllTokens(&out_map));
+ EXPECT_TRUE(out_map.empty());
+
+ // Check that you can still add it back in
+ EXPECT_TRUE(db.SetTokenForService(service, "cheese"));
+ EXPECT_TRUE(db.GetAllTokens(&out_map));
+ EXPECT_EQ(out_map.find(service)->second, "cheese");
+}
+
+TEST_F(WebDatabaseTest, TokenServiceGetSet) {
+ WebDatabase db;
+ ASSERT_EQ(sql::INIT_OK, db.Init(file_));
+
+ std::map<std::string, std::string> out_map;
+ std::string service;
+ service = "testservice";
+
+ EXPECT_TRUE(db.GetAllTokens(&out_map));
+ EXPECT_TRUE(out_map.empty());
+
+ EXPECT_TRUE(db.SetTokenForService(service, "pepperoni"));
+ EXPECT_TRUE(db.GetAllTokens(&out_map));
+ EXPECT_EQ(out_map.find(service)->second, "pepperoni");
+ out_map.clear();
+
+ // try blanking it - won't remove it from the db though!
+ EXPECT_TRUE(db.SetTokenForService(service, ""));
+ EXPECT_TRUE(db.GetAllTokens(&out_map));
+ EXPECT_EQ(out_map.find(service)->second, "");
+ out_map.clear();
+
+ // try mutating it
+ EXPECT_TRUE(db.SetTokenForService(service, "ham"));
+ EXPECT_TRUE(db.GetAllTokens(&out_map));
+ EXPECT_EQ(out_map.find(service)->second, "ham");
+}
+
TEST_F(WebDatabaseTest, AutoFillProfile) {
WebDatabase db;
diff --git a/chrome/test/signaling_task.h b/chrome/test/signaling_task.h
new file mode 100644
index 0000000..23063e1
--- /dev/null
+++ b/chrome/test/signaling_task.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2010 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.
+
+// A simple callback that you can use to wait for events on a thread.
+
+#ifndef CHROME_TEST_SIGNALING_TASK_H_
+#define CHROME_TEST_SIGNALING_TASK_H_
+#pragma once
+
+#include "base/waitable_event.h"
+
+using base::WaitableEvent;
+
+class SignalingTask : public Task {
+ public:
+ explicit SignalingTask(WaitableEvent* event) : event_(event) {
+ }
+
+ virtual void Run() {
+ event_->Signal();
+ }
+
+ private:
+ WaitableEvent* event_;
+};
+
+#endif // CHROME_TEST_SIGNALING_TASK_H_
diff --git a/chrome/test/testing_profile.cc b/chrome/test/testing_profile.cc
index e0a3968..6292079 100644
--- a/chrome/test/testing_profile.cc
+++ b/chrome/test/testing_profile.cc
@@ -169,6 +169,15 @@ TestingProfile::TestingProfile(int count)
file_util::CreateDirectory(path_);
}
+TestingProfile::TestingProfile(const FilePath& path)
+ : start_time_(Time::Now()),
+ created_theme_provider_(false),
+ has_history_service_(false),
+ off_the_record_(false),
+ last_session_exited_cleanly_(true) {
+ path_ = path;
+}
+
TestingProfile::~TestingProfile() {
NotificationService::current()->Notify(
NotificationType::PROFILE_DESTROYED,
diff --git a/chrome/test/testing_profile.h b/chrome/test/testing_profile.h
index ee4b601..1e39f8a 100644
--- a/chrome/test/testing_profile.h
+++ b/chrome/test/testing_profile.h
@@ -45,6 +45,12 @@ class TestingProfile : public Profile {
// time.
explicit TestingProfile(int count);
+ // Creates a new profile specifying the target directory.
+ // Use this as a temporary solution for tests requiring a ScopedTempDir.
+ // This directory must already exist.
+ // TODO(chron): Use a ScopedTempDir. Remove constructor. BUG=51833
+ explicit TestingProfile(const FilePath& directory);
+
virtual ~TestingProfile();
// Creates the favicon service. Consequent calls would recreate the service.