summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/browsing_data/browsing_data_remover.cc49
-rw-r--r--chrome/browser/browsing_data/browsing_data_remover.h4
-rw-r--r--content/browser/media/webrtc_identity_store.cc184
-rw-r--r--content/browser/media/webrtc_identity_store.h53
-rw-r--r--content/browser/media/webrtc_identity_store_backend.cc543
-rw-r--r--content/browser/media/webrtc_identity_store_backend.h111
-rw-r--r--content/browser/media/webrtc_identity_store_unittest.cc260
-rw-r--r--content/browser/renderer_host/media/webrtc_identity_service_host.cc14
-rw-r--r--content/browser/renderer_host/media/webrtc_identity_service_host.h4
-rw-r--r--content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc34
-rw-r--r--content/browser/renderer_host/render_process_host_impl.cc2
-rw-r--r--content/browser/storage_partition_impl.cc27
-rw-r--r--content/browser/storage_partition_impl.h4
-rw-r--r--content/browser/storage_partition_impl_unittest.cc10
-rw-r--r--content/content_browser.gypi2
-rw-r--r--content/public/browser/storage_partition.h2
16 files changed, 1182 insertions, 121 deletions
diff --git a/chrome/browser/browsing_data/browsing_data_remover.cc b/chrome/browser/browsing_data/browsing_data_remover.cc
index 008d5cf..b0a38f4 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover.cc
@@ -173,6 +173,7 @@ BrowsingDataRemover::BrowsingDataRemover(Profile* profile,
waiting_for_clear_server_bound_certs_(false),
waiting_for_clear_session_storage_(false),
waiting_for_clear_shader_cache_(false),
+ waiting_for_clear_webrtc_identity_store_(false),
remove_mask_(0),
remove_origin_(GURL()),
origin_set_mask_(0) {
@@ -545,6 +546,15 @@ void BrowsingDataRemover::RemoveImpl(int remove_mask,
content::RecordAction(UserMetricsAction("ClearBrowsingData_ShaderCache"));
ClearShaderCacheOnUIThread();
+
+ waiting_for_clear_webrtc_identity_store_ = true;
+ BrowserContext::GetDefaultStoragePartition(profile_)->ClearDataForRange(
+ content::StoragePartition::REMOVE_DATA_MASK_WEBRTC_IDENTITY,
+ content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
+ delete_begin_,
+ delete_end_,
+ base::Bind(&BrowsingDataRemover::OnClearWebRTCIdentityStore,
+ base::Unretained(this)));
}
#if defined(ENABLE_PLUGINS)
@@ -617,24 +627,21 @@ base::Time BrowsingDataRemover::CalculateBeginDeleteTime(
}
bool BrowsingDataRemover::AllDone() {
- return registrar_.IsEmpty() &&
- !waiting_for_clear_autofill_origin_urls_ &&
- !waiting_for_clear_cache_ &&
- !waiting_for_clear_nacl_cache_ &&
- !waiting_for_clear_cookies_count_&&
- !waiting_for_clear_history_ &&
- !waiting_for_clear_local_storage_ &&
- !waiting_for_clear_logged_in_predictor_ &&
- !waiting_for_clear_session_storage_ &&
- !waiting_for_clear_networking_history_ &&
- !waiting_for_clear_server_bound_certs_ &&
- !waiting_for_clear_plugin_data_ &&
- !waiting_for_clear_quota_managed_data_ &&
- !waiting_for_clear_content_licenses_ &&
- !waiting_for_clear_form_ &&
- !waiting_for_clear_hostname_resolution_cache_ &&
- !waiting_for_clear_network_predictor_ &&
- !waiting_for_clear_shader_cache_;
+ return registrar_.IsEmpty() && !waiting_for_clear_autofill_origin_urls_ &&
+ !waiting_for_clear_cache_ && !waiting_for_clear_nacl_cache_ &&
+ !waiting_for_clear_cookies_count_ && !waiting_for_clear_history_ &&
+ !waiting_for_clear_local_storage_ &&
+ !waiting_for_clear_logged_in_predictor_ &&
+ !waiting_for_clear_session_storage_ &&
+ !waiting_for_clear_networking_history_ &&
+ !waiting_for_clear_server_bound_certs_ &&
+ !waiting_for_clear_plugin_data_ &&
+ !waiting_for_clear_quota_managed_data_ &&
+ !waiting_for_clear_content_licenses_ && !waiting_for_clear_form_ &&
+ !waiting_for_clear_hostname_resolution_cache_ &&
+ !waiting_for_clear_network_predictor_ &&
+ !waiting_for_clear_shader_cache_ &&
+ !waiting_for_clear_webrtc_identity_store_;
}
void BrowsingDataRemover::Observe(int type,
@@ -1123,3 +1130,9 @@ void BrowsingDataRemover::OnClearedAutofillOriginURLs() {
waiting_for_clear_autofill_origin_urls_ = false;
NotifyAndDeleteIfDone();
}
+
+void BrowsingDataRemover::OnClearWebRTCIdentityStore() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ waiting_for_clear_webrtc_identity_store_ = false;
+ NotifyAndDeleteIfDone();
+}
diff --git a/chrome/browser/browsing_data/browsing_data_remover.h b/chrome/browser/browsing_data/browsing_data_remover.h
index af1b441..d597d9d 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.h
+++ b/chrome/browser/browsing_data/browsing_data_remover.h
@@ -361,6 +361,9 @@ class BrowsingDataRemover : public content::NotificationObserver
// Invoked on the IO thread to delete from the shader cache.
void ClearShaderCacheOnUIThread();
+ // Callback on UI thread when the WebRTC identities are cleared.
+ void OnClearWebRTCIdentityStore();
+
// Returns true if we're all done.
bool AllDone();
@@ -425,6 +428,7 @@ class BrowsingDataRemover : public content::NotificationObserver
bool waiting_for_clear_server_bound_certs_;
bool waiting_for_clear_session_storage_;
bool waiting_for_clear_shader_cache_;
+ bool waiting_for_clear_webrtc_identity_store_;
// Tracking how many origins need to be deleted, and whether we're finished
// gathering origins.
diff --git a/content/browser/media/webrtc_identity_store.cc b/content/browser/media/webrtc_identity_store.cc
index ee19f1e..1c77862 100644
--- a/content/browser/media/webrtc_identity_store.cc
+++ b/content/browser/media/webrtc_identity_store.cc
@@ -4,11 +4,14 @@
#include "content/browser/media/webrtc_identity_store.h"
+#include <map>
+
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/threading/worker_pool.h"
+#include "content/browser/media/webrtc_identity_store_backend.h"
#include "content/public/browser/browser_thread.h"
#include "crypto/rsa_private_key.h"
#include "net/base/net_errors.h"
@@ -18,11 +21,18 @@
namespace content {
struct WebRTCIdentityRequestResult {
+ WebRTCIdentityRequestResult(int error,
+ const std::string& certificate,
+ const std::string& private_key)
+ : error(error), certificate(certificate), private_key(private_key) {}
+
int error;
std::string certificate;
std::string private_key;
};
+// Generates a new identity using |common_name| and returns the result in
+// |result|.
static void GenerateIdentityWorker(const std::string& common_name,
WebRTCIdentityRequestResult* result) {
result->error = net::OK;
@@ -60,33 +70,57 @@ static void GenerateIdentityWorker(const std::string& common_name,
std::string(private_key_info.begin(), private_key_info.end());
}
+class WebRTCIdentityRequestHandle;
+
// The class represents an identity request internal to WebRTCIdentityStore.
-// It has a one-to-one mapping to the external version of the request
-// WebRTCIdentityRequestHandle, which is the target of the
-// WebRTCIdentityRequest's completion callback.
+// It has a one-to-many mapping to the external version of the request,
+// WebRTCIdentityRequestHandle, i.e. multiple identical external requests are
+// combined into one internal request.
// It's deleted automatically when the request is completed.
class WebRTCIdentityRequest {
public:
- WebRTCIdentityRequest(const WebRTCIdentityStore::CompletionCallback& callback)
- : callback_(callback) {}
+ WebRTCIdentityRequest(const GURL& origin,
+ const std::string& identity_name,
+ const std::string& common_name)
+ : origin_(origin),
+ identity_name_(identity_name),
+ common_name_(common_name) {}
- void Cancel() {
+ void Cancel(WebRTCIdentityRequestHandle* handle) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- callback_.Reset();
+ if (callbacks_.find(handle) == callbacks_.end())
+ return;
+ callbacks_.erase(handle);
}
private:
friend class WebRTCIdentityStore;
- void Post(WebRTCIdentityRequestResult* result) {
+ void AddCallback(WebRTCIdentityRequestHandle* handle,
+ const WebRTCIdentityStore::CompletionCallback& callback) {
+ DCHECK(callbacks_.find(handle) == callbacks_.end());
+ callbacks_[handle] = callback;
+ }
+
+ // This method deletes "this" and no one should access it after the request
+ // completes.
+ // We do not use base::Owned to tie its lifetime to the callback for
+ // WebRTCIdentityStoreBackend::FindIdentity, because it needs to live longer
+ // than that if the identity does not exist in DB.
+ void Post(const WebRTCIdentityRequestResult& result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (callback_.is_null())
- return;
- callback_.Run(result->error, result->certificate, result->private_key);
- // "this" will be deleted after this point.
+ for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end();
+ ++it)
+ it->second.Run(result.error, result.certificate, result.private_key);
+ delete this;
}
- WebRTCIdentityStore::CompletionCallback callback_;
+ GURL origin_;
+ std::string identity_name_;
+ std::string common_name_;
+ typedef std::map<WebRTCIdentityRequestHandle*,
+ WebRTCIdentityStore::CompletionCallback> CallbackMap;
+ CallbackMap callbacks_;
};
// The class represents an identity request which calls back to the external
@@ -115,7 +149,7 @@ class WebRTCIdentityRequestHandle {
request_ = NULL;
// "this" will be deleted after the following call, because "this" is
// owned by the Callback held by |request|.
- request->Cancel();
+ request->Cancel(this);
}
void OnRequestStarted(WebRTCIdentityRequest* request) {
@@ -140,10 +174,12 @@ class WebRTCIdentityRequestHandle {
DISALLOW_COPY_AND_ASSIGN(WebRTCIdentityRequestHandle);
};
-WebRTCIdentityStore::WebRTCIdentityStore()
- : task_runner_(base::WorkerPool::GetTaskRunner(true)) {}
+WebRTCIdentityStore::WebRTCIdentityStore(const base::FilePath& path,
+ quota::SpecialStoragePolicy* policy)
+ : task_runner_(base::WorkerPool::GetTaskRunner(true)),
+ backend_(new WebRTCIdentityStoreBackend(path, policy)) {}
-WebRTCIdentityStore::~WebRTCIdentityStore() {}
+WebRTCIdentityStore::~WebRTCIdentityStore() { backend_->Close(); }
base::Closure WebRTCIdentityStore::RequestIdentity(
const GURL& origin,
@@ -155,26 +191,118 @@ base::Closure WebRTCIdentityStore::RequestIdentity(
WebRTCIdentityRequestHandle* handle =
new WebRTCIdentityRequestHandle(this, callback);
- WebRTCIdentityRequest* request = new WebRTCIdentityRequest(base::Bind(
- &WebRTCIdentityRequestHandle::OnRequestComplete, base::Owned(handle)));
- handle->OnRequestStarted(request);
+ WebRTCIdentityRequest* request =
+ FindRequest(origin, identity_name, common_name);
- WebRTCIdentityRequestResult* result = new WebRTCIdentityRequestResult();
- if (!task_runner_->PostTaskAndReply(
- FROM_HERE,
- base::Bind(&GenerateIdentityWorker, common_name, result),
- base::Bind(&WebRTCIdentityRequest::Post,
- base::Owned(request),
- base::Owned(result))))
- return base::Closure();
+ // If there is no identical request in flight, create a new one, queue it,
+ // and make the backend request.
+ if (!request) {
+ request = new WebRTCIdentityRequest(origin, identity_name, common_name);
+ if (!backend_->FindIdentity(
+ origin,
+ identity_name,
+ common_name,
+ base::Bind(
+ &WebRTCIdentityStore::BackendFindCallback, this, request))) {
+ delete request;
+ return base::Closure();
+ }
+ in_flight_requests_.push_back(request);
+ }
+
+ request->AddCallback(
+ handle,
+ base::Bind(&WebRTCIdentityRequestHandle::OnRequestComplete,
+ base::Owned(handle)));
+ handle->OnRequestStarted(request);
return base::Bind(&WebRTCIdentityRequestHandle::Cancel,
base::Unretained(handle));
}
+void WebRTCIdentityStore::DeleteBetween(base::Time delete_begin,
+ base::Time delete_end,
+ const base::Closure& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ backend_->DeleteBetween(delete_begin, delete_end, callback);
+}
+
void WebRTCIdentityStore::SetTaskRunnerForTesting(
const scoped_refptr<base::TaskRunner>& task_runner) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
task_runner_ = task_runner;
}
+void WebRTCIdentityStore::BackendFindCallback(WebRTCIdentityRequest* request,
+ int error,
+ const std::string& certificate,
+ const std::string& private_key) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (error == net::OK) {
+ DVLOG(2) << "Identity found in DB.";
+ WebRTCIdentityRequestResult result(error, certificate, private_key);
+ PostRequestResult(request, result);
+ return;
+ }
+ // Generate a new identity if not found in the DB.
+ WebRTCIdentityRequestResult* result =
+ new WebRTCIdentityRequestResult(0, "", "");
+ if (!task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&GenerateIdentityWorker, request->common_name_, result),
+ base::Bind(&WebRTCIdentityStore::GenerateIdentityCallback,
+ this,
+ request,
+ base::Owned(result)))) {
+ // Completes the request with error if failed to post the task.
+ WebRTCIdentityRequestResult result(net::ERR_UNEXPECTED, "", "");
+ PostRequestResult(request, result);
+ }
+}
+
+void WebRTCIdentityStore::GenerateIdentityCallback(
+ WebRTCIdentityRequest* request,
+ WebRTCIdentityRequestResult* result) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (result->error == net::OK) {
+ DVLOG(2) << "New identity generated and added to the backend.";
+ backend_->AddIdentity(request->origin_,
+ request->identity_name_,
+ request->common_name_,
+ result->certificate,
+ result->private_key);
+ }
+ PostRequestResult(request, *result);
+}
+
+void WebRTCIdentityStore::PostRequestResult(
+ WebRTCIdentityRequest* request,
+ const WebRTCIdentityRequestResult& result) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ // Removes the in flight request from the queue.
+ for (size_t i = 0; i < in_flight_requests_.size(); ++i) {
+ if (in_flight_requests_[i] == request) {
+ in_flight_requests_.erase(in_flight_requests_.begin() + i);
+ break;
+ }
+ }
+ // |request| will be deleted after this call.
+ request->Post(result);
+}
+
+// Find an identical request from the in flight requests.
+WebRTCIdentityRequest* WebRTCIdentityStore::FindRequest(
+ const GURL& origin,
+ const std::string& identity_name,
+ const std::string& common_name) {
+ for (size_t i = 0; i < in_flight_requests_.size(); ++i) {
+ if (in_flight_requests_[i]->origin_ == origin &&
+ in_flight_requests_[i]->identity_name_ == identity_name &&
+ in_flight_requests_[i]->common_name_ == common_name) {
+ return in_flight_requests_[i];
+ }
+ }
+ return NULL;
+}
+
} // namespace content
diff --git a/content/browser/media/webrtc_identity_store.h b/content/browser/media/webrtc_identity_store.h
index 1649384..c2b523e 100644
--- a/content/browser/media/webrtc_identity_store.h
+++ b/content/browser/media/webrtc_identity_store.h
@@ -6,32 +6,44 @@
#define CONTENT_BROWSER_MEDIA_WEBRTC_IDENTITY_STORE_H_
#include <string>
+#include <vector>
#include "base/callback.h"
+#include "base/time/time.h"
#include "content/common/content_export.h"
class GURL;
namespace base {
+class FilePath;
class TaskRunner;
} // namespace base
-namespace content {
+namespace quota {
+class SpecialStoragePolicy;
+} // namespace quota
+namespace content {
+class WebRTCIdentityRequest;
+struct WebRTCIdentityRequestResult;
+class WebRTCIdentityStoreBackend;
class WebRTCIdentityStoreTest;
// A class for creating and fetching DTLS identities, i.e. the private key and
// the self-signed certificate.
-class CONTENT_EXPORT WebRTCIdentityStore {
+// It can be created/destroyed on any thread, but the public methods must be
+// called on the IO thread.
+class CONTENT_EXPORT WebRTCIdentityStore
+ : public base::RefCountedThreadSafe<WebRTCIdentityStore> {
public:
typedef base::Callback<void(int error,
const std::string& certificate,
const std::string& private_key)>
CompletionCallback;
- WebRTCIdentityStore();
- // Only virtual to allow subclassing for test mock.
- virtual ~WebRTCIdentityStore();
+ // If |path| is empty, nothing will be saved to disk.
+ WebRTCIdentityStore(const base::FilePath& path,
+ quota::SpecialStoragePolicy* policy);
// Retrieve the cached DTLS private key and certificate, i.e. identity, for
// the |origin| and |identity_name| pair, or generate a new identity using
@@ -39,7 +51,6 @@ class CONTENT_EXPORT WebRTCIdentityStore {
// If the given |common_name| is different from the common name in the cached
// identity that has the same origin and identity_name, a new private key and
// a new certificate will be generated, overwriting the old one.
- // TODO(jiayl): implement identity caching through a persistent storage.
//
// |origin| is the origin of the DTLS connection;
// |identity_name| is used to identify an identity within an origin; it is
@@ -49,7 +60,7 @@ class CONTENT_EXPORT WebRTCIdentityStore {
// be shared with the peer of the DTLS connection. Identities created for
// different origins or different identity names may have the same common
// name.
- // |callback| is the callback to return the result.
+ // |callback| is the callback to return the result as DER strings.
//
// Returns the Closure used to cancel the request if the request is accepted.
// The Closure can only be called before the request completes.
@@ -58,14 +69,42 @@ class CONTENT_EXPORT WebRTCIdentityStore {
const std::string& common_name,
const CompletionCallback& callback);
+ // Delete the identities created between |delete_begin| and |delete_end|.
+ // |callback| will be called when the operation is done.
+ void DeleteBetween(base::Time delete_begin,
+ base::Time delete_end,
+ const base::Closure& callback);
+
+ protected:
+ // Only virtual to allow subclassing for test mock.
+ virtual ~WebRTCIdentityStore();
+
private:
+ friend class base::RefCountedThreadSafe<WebRTCIdentityStore>;
friend class WebRTCIdentityStoreTest;
void SetTaskRunnerForTesting(
const scoped_refptr<base::TaskRunner>& task_runner);
+ void BackendFindCallback(WebRTCIdentityRequest* request,
+ int error,
+ const std::string& certificate,
+ const std::string& private_key);
+ void GenerateIdentityCallback(WebRTCIdentityRequest* request,
+ WebRTCIdentityRequestResult* result);
+ WebRTCIdentityRequest* FindRequest(const GURL& origin,
+ const std::string& identity_name,
+ const std::string& common_name);
+ void PostRequestResult(WebRTCIdentityRequest* request,
+ const WebRTCIdentityRequestResult& result);
+
// The TaskRunner for doing work on a worker thread.
scoped_refptr<base::TaskRunner> task_runner_;
+ // Weak references of the in flight requests. Used to join identical external
+ // requests.
+ std::vector<WebRTCIdentityRequest*> in_flight_requests_;
+
+ scoped_refptr<WebRTCIdentityStoreBackend> backend_;
DISALLOW_COPY_AND_ASSIGN(WebRTCIdentityStore);
};
diff --git a/content/browser/media/webrtc_identity_store_backend.cc b/content/browser/media/webrtc_identity_store_backend.cc
new file mode 100644
index 0000000..a86cc20
--- /dev/null
+++ b/content/browser/media/webrtc_identity_store_backend.cc
@@ -0,0 +1,543 @@
+// Copyright 2013 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 "content/browser/media/webrtc_identity_store_backend.h"
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/net_errors.h"
+#include "sql/error_delegate_util.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+#include "url/gurl.h"
+#include "webkit/browser/quota/special_storage_policy.h"
+
+namespace content {
+
+static const char* kWebRTCIdentityStoreDBName = "webrtc_identity_store";
+
+static const base::FilePath::CharType kWebRTCIdentityStoreDirectory[] =
+ FILE_PATH_LITERAL("WebRTCIdentityStore");
+
+// Initializes the identity table, returning true on success.
+static bool InitDB(sql::Connection* db) {
+ if (db->DoesTableExist(kWebRTCIdentityStoreDBName)) {
+ if (db->DoesColumnExist(kWebRTCIdentityStoreDBName, "origin") &&
+ db->DoesColumnExist(kWebRTCIdentityStoreDBName, "identity_name") &&
+ db->DoesColumnExist(kWebRTCIdentityStoreDBName, "common_name") &&
+ db->DoesColumnExist(kWebRTCIdentityStoreDBName, "certificate") &&
+ db->DoesColumnExist(kWebRTCIdentityStoreDBName, "private_key") &&
+ db->DoesColumnExist(kWebRTCIdentityStoreDBName, "creation_time"))
+ return true;
+ if (!db->Execute("DROP TABLE webrtc_identity_store"))
+ return false;
+ }
+
+ return db->Execute(
+ "CREATE TABLE webrtc_identity_store"
+ " ("
+ "origin TEXT NOT NULL,"
+ "identity_name TEXT NOT NULL,"
+ "common_name TEXT NOT NULL,"
+ "certificate BLOB NOT NULL,"
+ "private_key BLOB NOT NULL,"
+ "creation_time INTEGER)");
+}
+
+struct WebRTCIdentityStoreBackend::IdentityKey {
+ IdentityKey(const GURL& origin, const std::string& identity_name)
+ : origin(origin), identity_name(identity_name) {}
+
+ bool operator<(const IdentityKey& other) const {
+ return origin < other.origin ||
+ (origin == other.origin && identity_name < other.identity_name);
+ }
+
+ GURL origin;
+ std::string identity_name;
+};
+
+struct WebRTCIdentityStoreBackend::Identity {
+ Identity(const std::string& common_name,
+ const std::string& certificate,
+ const std::string& private_key)
+ : common_name(common_name),
+ certificate(certificate),
+ private_key(private_key),
+ creation_time(base::Time::Now().ToInternalValue()) {}
+
+ Identity(const std::string& common_name,
+ const std::string& certificate,
+ const std::string& private_key,
+ int64 creation_time)
+ : common_name(common_name),
+ certificate(certificate),
+ private_key(private_key),
+ creation_time(creation_time) {}
+
+ std::string common_name;
+ std::string certificate;
+ std::string private_key;
+ int64 creation_time;
+};
+
+struct WebRTCIdentityStoreBackend::PendingFindRequest {
+ PendingFindRequest(const GURL& origin,
+ const std::string& identity_name,
+ const std::string& common_name,
+ const FindIdentityCallback& callback)
+ : origin(origin),
+ identity_name(identity_name),
+ common_name(common_name),
+ callback(callback) {}
+
+ ~PendingFindRequest() {}
+
+ GURL origin;
+ std::string identity_name;
+ std::string common_name;
+ FindIdentityCallback callback;
+};
+
+// The class encapsulates the database operations. All members except ctor and
+// dtor should be accessed on the DB thread.
+// It can be created/destroyed on any thread.
+class WebRTCIdentityStoreBackend::SqlLiteStorage
+ : public base::RefCountedThreadSafe<SqlLiteStorage> {
+ public:
+ SqlLiteStorage(const base::FilePath& path,
+ quota::SpecialStoragePolicy* policy)
+ : special_storage_policy_(policy) {
+ if (!path.empty())
+ path_ = path.Append(kWebRTCIdentityStoreDirectory);
+ }
+
+ void Load(IdentityMap* out_map);
+ void Close();
+ void AddIdentity(const GURL& origin,
+ const std::string& identity_name,
+ const Identity& identity);
+ void DeleteIdentity(const GURL& origin,
+ const std::string& identity_name,
+ const Identity& identity);
+ void DeleteBetween(base::Time delete_begin,
+ base::Time delete_end,
+ const base::Closure& callback);
+
+ private:
+ friend class base::RefCountedThreadSafe<SqlLiteStorage>;
+
+ enum OperationType {
+ ADD_IDENTITY,
+ DELETE_IDENTITY
+ };
+ struct PendingOperation {
+ PendingOperation(OperationType type,
+ const GURL& origin,
+ const std::string& identity_name,
+ const Identity& identity)
+ : type(type),
+ origin(origin),
+ identity_name(identity_name),
+ identity(identity) {}
+
+ OperationType type;
+ GURL origin;
+ std::string identity_name;
+ Identity identity;
+ };
+ typedef std::vector<PendingOperation*> PendingOperationList;
+
+ virtual ~SqlLiteStorage() {}
+ void OnDatabaseError(int error, sql::Statement* stmt);
+ void BatchOperation(OperationType type,
+ const GURL& origin,
+ const std::string& identity_name,
+ const Identity& identity);
+ void Commit();
+
+ // The file path of the DB. Empty if temporary.
+ base::FilePath path_;
+ scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy_;
+ scoped_ptr<sql::Connection> db_;
+ // Batched DB operations pending to commit.
+ PendingOperationList pending_operations_;
+
+ DISALLOW_COPY_AND_ASSIGN(SqlLiteStorage);
+};
+
+WebRTCIdentityStoreBackend::WebRTCIdentityStoreBackend(
+ const base::FilePath& path,
+ quota::SpecialStoragePolicy* policy)
+ : state_(NOT_STARTED),
+ sql_lite_storage_(new SqlLiteStorage(path, policy)) {}
+
+bool WebRTCIdentityStoreBackend::FindIdentity(
+ const GURL& origin,
+ const std::string& identity_name,
+ const std::string& common_name,
+ const FindIdentityCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (state_ == CLOSED)
+ return false;
+
+ if (state_ != LOADED) {
+ // Queues the request to wait for the DB to load.
+ pending_find_requests_.push_back(
+ new PendingFindRequest(origin, identity_name, common_name, callback));
+ if (state_ == LOADING)
+ return true;
+
+ DCHECK_EQ(state_, NOT_STARTED);
+
+ // Kick off loading the DB.
+ scoped_ptr<IdentityMap> out_map(new IdentityMap());
+ if (BrowserThread::PostTaskAndReply(
+ BrowserThread::DB,
+ FROM_HERE,
+ base::Bind(&SqlLiteStorage::Load, sql_lite_storage_, out_map.get()),
+ base::Bind(&WebRTCIdentityStoreBackend::OnLoaded,
+ this,
+ base::Passed(&out_map)))) {
+ state_ = LOADING;
+ return true;
+ }
+ // If it fails to post task, falls back to ERR_FILE_NOT_FOUND.
+ }
+
+ IdentityKey key(origin, identity_name);
+ IdentityMap::iterator iter = identities_.find(key);
+ if (iter != identities_.end() && iter->second.common_name == common_name) {
+ // Identity found.
+ return BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(callback,
+ net::OK,
+ iter->second.certificate,
+ iter->second.private_key));
+ }
+
+ return BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(callback, net::ERR_FILE_NOT_FOUND, "", ""));
+}
+
+void WebRTCIdentityStoreBackend::AddIdentity(const GURL& origin,
+ const std::string& identity_name,
+ const std::string& common_name,
+ const std::string& certificate,
+ const std::string& private_key) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (state_ == CLOSED)
+ return;
+
+ // If there is an existing identity for the same origin and identity_name,
+ // delete it.
+ IdentityKey key(origin, identity_name);
+ Identity identity(common_name, certificate, private_key);
+
+ if (identities_.find(key) != identities_.end()) {
+ if (!BrowserThread::PostTask(BrowserThread::DB,
+ FROM_HERE,
+ base::Bind(&SqlLiteStorage::DeleteIdentity,
+ sql_lite_storage_,
+ origin,
+ identity_name,
+ identities_.find(key)->second)))
+ return;
+ }
+ identities_.insert(std::pair<IdentityKey, Identity>(key, identity));
+
+ BrowserThread::PostTask(BrowserThread::DB,
+ FROM_HERE,
+ base::Bind(&SqlLiteStorage::AddIdentity,
+ sql_lite_storage_,
+ origin,
+ identity_name,
+ identity));
+}
+
+void WebRTCIdentityStoreBackend::Close() {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&WebRTCIdentityStoreBackend::Close, this));
+ return;
+ }
+
+ if (state_ == CLOSED)
+ return;
+
+ state_ = CLOSED;
+ BrowserThread::PostTask(
+ BrowserThread::DB,
+ FROM_HERE,
+ base::Bind(&SqlLiteStorage::Close, sql_lite_storage_));
+}
+
+void WebRTCIdentityStoreBackend::DeleteBetween(base::Time delete_begin,
+ base::Time delete_end,
+ const base::Closure& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ // Delete the in-memory cache.
+ IdentityMap::iterator it = identities_.begin();
+ while (it != identities_.end()) {
+ if (it->second.creation_time >= delete_begin.ToInternalValue() &&
+ it->second.creation_time <= delete_end.ToInternalValue())
+ identities_.erase(it++);
+ else
+ it++;
+ }
+
+ BrowserThread::PostTaskAndReply(BrowserThread::DB,
+ FROM_HERE,
+ base::Bind(&SqlLiteStorage::DeleteBetween,
+ sql_lite_storage_,
+ delete_begin,
+ delete_end,
+ callback),
+ callback);
+}
+
+WebRTCIdentityStoreBackend::~WebRTCIdentityStoreBackend() {}
+
+void WebRTCIdentityStoreBackend::OnLoaded(scoped_ptr<IdentityMap> out_map) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ state_ = LOADED;
+ identities_.swap(*out_map);
+
+ for (size_t i = 0; i < pending_find_requests_.size(); ++i) {
+ FindIdentity(pending_find_requests_[i]->origin,
+ pending_find_requests_[i]->identity_name,
+ pending_find_requests_[i]->common_name,
+ pending_find_requests_[i]->callback);
+ delete pending_find_requests_[i];
+ }
+ pending_find_requests_.clear();
+}
+
+//
+// Implementation of SqlLiteStorage.
+//
+
+void WebRTCIdentityStoreBackend::SqlLiteStorage::Load(IdentityMap* out_map) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ DCHECK(!db_.get());
+
+ // Ensure the parent directory for storing certs is created before reading
+ // from it.
+ const base::FilePath dir = path_.DirName();
+ if (!base::PathExists(dir) && !file_util::CreateDirectory(dir)) {
+ DLOG(ERROR) << "Unable to open DB file path.";
+ return;
+ }
+
+ db_.reset(new sql::Connection());
+
+ db_->set_error_callback(base::Bind(&SqlLiteStorage::OnDatabaseError, this));
+
+ if (!db_->Open(path_)) {
+ DLOG(ERROR) << "Unable to open DB.";
+ db_.reset();
+ return;
+ }
+
+ if (!InitDB(db_.get())) {
+ DLOG(ERROR) << "Unable to init DB.";
+ db_.reset();
+ return;
+ }
+
+ db_->Preload();
+
+ // Slurp all the identities into the out_map.
+ sql::Statement stmt(db_->GetUniqueStatement(
+ "SELECT origin, identity_name, common_name, "
+ "certificate, private_key, creation_time "
+ "FROM webrtc_identity_store"));
+ CHECK(stmt.is_valid());
+
+ while (stmt.Step()) {
+ IdentityKey key(GURL(stmt.ColumnString(0)), stmt.ColumnString(1));
+ std::string common_name(stmt.ColumnString(2));
+ std::string cert, private_key;
+ stmt.ColumnBlobAsString(3, &cert);
+ stmt.ColumnBlobAsString(4, &private_key);
+ int64 creation_time = stmt.ColumnInt64(5);
+ std::pair<IdentityMap::iterator, bool> result =
+ out_map->insert(std::pair<IdentityKey, Identity>(
+ key, Identity(common_name, cert, private_key, creation_time)));
+ DCHECK(result.second);
+ }
+}
+
+void WebRTCIdentityStoreBackend::SqlLiteStorage::Close() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ Commit();
+ db_.reset();
+}
+
+void WebRTCIdentityStoreBackend::SqlLiteStorage::AddIdentity(
+ const GURL& origin,
+ const std::string& identity_name,
+ const Identity& identity) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!db_.get())
+ return;
+
+ // Do not add for session only origins.
+ if (special_storage_policy_.get() &&
+ !special_storage_policy_->IsStorageProtected(origin) &&
+ special_storage_policy_->IsStorageSessionOnly(origin)) {
+ return;
+ }
+ BatchOperation(ADD_IDENTITY, origin, identity_name, identity);
+}
+
+void WebRTCIdentityStoreBackend::SqlLiteStorage::DeleteIdentity(
+ const GURL& origin,
+ const std::string& identity_name,
+ const Identity& identity) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!db_.get())
+ return;
+ BatchOperation(DELETE_IDENTITY, origin, identity_name, identity);
+}
+
+void WebRTCIdentityStoreBackend::SqlLiteStorage::OnDatabaseError(
+ int error,
+ sql::Statement* stmt) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!sql::IsErrorCatastrophic(error))
+ return;
+ db_->RazeAndClose();
+}
+
+void WebRTCIdentityStoreBackend::SqlLiteStorage::BatchOperation(
+ OperationType type,
+ const GURL& origin,
+ const std::string& identity_name,
+ const Identity& identity) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ // Commit every 30 seconds.
+ static const base::TimeDelta kCommitInterval(
+ base::TimeDelta::FromSeconds(30));
+ // Commit right away if we have more than 512 outstanding operations.
+ static const size_t kCommitAfterBatchSize = 512;
+
+ // We do a full copy of the cert here, and hopefully just here.
+ scoped_ptr<PendingOperation> operation(
+ new PendingOperation(type, origin, identity_name, identity));
+
+ pending_operations_.push_back(operation.release());
+
+ if (pending_operations_.size() == 1) {
+ // We've gotten our first entry for this batch, fire off the timer.
+ BrowserThread::PostDelayedTask(BrowserThread::DB,
+ FROM_HERE,
+ base::Bind(&SqlLiteStorage::Commit, this),
+ kCommitInterval);
+ } else if (pending_operations_.size() >= kCommitAfterBatchSize) {
+ // We've reached a big enough batch, fire off a commit now.
+ BrowserThread::PostTask(BrowserThread::DB,
+ FROM_HERE,
+ base::Bind(&SqlLiteStorage::Commit, this));
+ }
+}
+
+void WebRTCIdentityStoreBackend::SqlLiteStorage::Commit() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ // Maybe an old timer fired or we are already Close()'ed.
+ if (!db_.get() || pending_operations_.empty())
+ return;
+
+ sql::Statement add_stmt(db_->GetCachedStatement(
+ SQL_FROM_HERE,
+ "INSERT INTO webrtc_identity_store "
+ "(origin, identity_name, common_name, certificate,"
+ " private_key, creation_time) VALUES"
+ " (?,?,?,?,?,?)"));
+
+ CHECK(add_stmt.is_valid());
+
+ sql::Statement del_stmt(db_->GetCachedStatement(
+ SQL_FROM_HERE,
+ "DELETE FROM webrtc_identity_store WHERE origin=? AND identity_name=?"));
+
+ CHECK(del_stmt.is_valid());
+
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin()) {
+ DLOG(ERROR) << "Failed to begin the transaction.";
+ return;
+ }
+
+ for (PendingOperationList::iterator it = pending_operations_.begin();
+ it != pending_operations_.end();
+ ++it) {
+ scoped_ptr<PendingOperation> po(*it);
+ switch (po->type) {
+ case ADD_IDENTITY: {
+ add_stmt.Reset(true);
+ add_stmt.BindString(0, po->origin.spec());
+ add_stmt.BindString(1, po->identity_name);
+ add_stmt.BindString(2, po->identity.common_name);
+ const std::string& cert = po->identity.certificate;
+ add_stmt.BindBlob(3, cert.data(), cert.size());
+ const std::string& private_key = po->identity.private_key;
+ add_stmt.BindBlob(4, private_key.data(), private_key.size());
+ add_stmt.BindInt64(5, po->identity.creation_time);
+ CHECK(add_stmt.Run());
+ break;
+ }
+ case DELETE_IDENTITY:
+ del_stmt.Reset(true);
+ del_stmt.BindString(0, po->origin.spec());
+ add_stmt.BindString(1, po->identity_name);
+ CHECK(del_stmt.Run());
+ break;
+
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+ transaction.Commit();
+ pending_operations_.clear();
+}
+
+void WebRTCIdentityStoreBackend::SqlLiteStorage::DeleteBetween(
+ base::Time delete_begin,
+ base::Time delete_end,
+ const base::Closure& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!db_.get())
+ return;
+
+ // Commit pending operations first.
+ Commit();
+
+ sql::Statement del_stmt(db_->GetCachedStatement(
+ SQL_FROM_HERE,
+ "DELETE FROM webrtc_identity_store"
+ " WHERE creation_time >= ? AND creation_time <= ?"));
+ CHECK(del_stmt.is_valid());
+
+ del_stmt.BindInt64(0, delete_begin.ToInternalValue());
+ del_stmt.BindInt64(1, delete_end.ToInternalValue());
+
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin()) {
+ DLOG(ERROR) << "Failed to begin the transaction.";
+ return;
+ }
+
+ CHECK(del_stmt.Run());
+ transaction.Commit();
+}
+
+} // namespace content
diff --git a/content/browser/media/webrtc_identity_store_backend.h b/content/browser/media/webrtc_identity_store_backend.h
new file mode 100644
index 0000000..ab4e1ed
--- /dev/null
+++ b/content/browser/media/webrtc_identity_store_backend.h
@@ -0,0 +1,111 @@
+// Copyright 2013 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.
+
+#ifndef CONTENT_BROWSER_MEDIA_WEBRTC_IDENTITY_STORE_BACKEND_H_
+#define CONTENT_BROWSER_MEDIA_WEBRTC_IDENTITY_STORE_BACKEND_H_
+
+#include <map>
+#include <string>
+
+#include "base/time/time.h"
+#include "sql/connection.h"
+#include "sql/meta_table.h"
+
+class GURL;
+
+namespace base {
+class FilePath;
+} // namespace base
+
+namespace quota {
+class SpecialStoragePolicy;
+} // namespace quota
+
+namespace content {
+
+// This class represents a persistent cache of WebRTC identities.
+// It can be created/destroyed/Close() on any thread. All other members should
+// be accessed on the IO thread.
+class WebRTCIdentityStoreBackend
+ : public base::RefCountedThreadSafe<WebRTCIdentityStoreBackend> {
+ public:
+ typedef base::Callback<void(int error,
+ const std::string& certificate,
+ const std::string& private_key)>
+ FindIdentityCallback;
+
+ // No data is saved on disk if |path| is empty.
+ WebRTCIdentityStoreBackend(const base::FilePath& path,
+ quota::SpecialStoragePolicy* policy);
+
+ // Finds the identity with |origin|, |identity_name|, and |common_name| from
+ // the DB.
+ // |origin| is the origin of the identity;
+ // |identity_name| is used to identify an identity within an origin;
+ // |common_name| is the common name used to generate the certificate;
+ // |callback| is the callback to return the find result.
+ // Returns true if |callback| will be called.
+ // Should be called on the IO thread.
+ bool FindIdentity(const GURL& origin,
+ const std::string& identity_name,
+ const std::string& common_name,
+ const FindIdentityCallback& callback);
+
+ // Adds the identity to the DB and overwrites any existing identity having the
+ // same origin and identity_name.
+ // |origin| is the origin of the identity;
+ // |identity_name| is used to identify an identity within an origin;
+ // |common_name| is the common name used to generate the certificate;
+ // |certificate| is the DER string of the certificate;
+ // |private_key| is the DER string of the private key.
+ // Should be called on the IO thread.
+ void AddIdentity(const GURL& origin,
+ const std::string& identity_name,
+ const std::string& common_name,
+ const std::string& certificate,
+ const std::string& private_key);
+
+ // Commits all pending DB operations and closes the DB connection. Any API
+ // call after this will fail.
+ // Can be called on any thread.
+ void Close();
+
+ // Delete the data created between |delete_begin| and |delete_end|.
+ // Should be called on the IO thread.
+ void DeleteBetween(base::Time delete_begin,
+ base::Time delete_end,
+ const base::Closure& callback);
+
+ private:
+ friend class base::RefCountedThreadSafe<WebRTCIdentityStoreBackend>;
+ class SqlLiteStorage;
+ enum LoadingState {
+ NOT_STARTED,
+ LOADING,
+ LOADED,
+ CLOSED,
+ };
+ struct PendingFindRequest;
+ struct IdentityKey;
+ struct Identity;
+ typedef std::map<IdentityKey, Identity> IdentityMap;
+
+ ~WebRTCIdentityStoreBackend();
+
+ void OnLoaded(scoped_ptr<IdentityMap> out_map);
+
+ // In-memory copy of the identities.
+ IdentityMap identities_;
+ // "Find identity" requests waiting for the DB to load.
+ std::vector<PendingFindRequest*> pending_find_requests_;
+ // The persistent storage loading state.
+ LoadingState state_;
+ // The persistent storage of identities.
+ scoped_refptr<SqlLiteStorage> sql_lite_storage_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebRTCIdentityStoreBackend);
+};
+}
+
+#endif // CONTENT_BROWSER_MEDIA_WEBRTC_IDENTITY_STORE_BACKEND_H_
diff --git a/content/browser/media/webrtc_identity_store_unittest.cc b/content/browser/media/webrtc_identity_store_unittest.cc
index a975dd4..81c25d7 100644
--- a/content/browser/media/webrtc_identity_store_unittest.cc
+++ b/content/browser/media/webrtc_identity_store_unittest.cc
@@ -17,15 +17,37 @@ namespace content {
// TODO(jiayl): the tests fail on Android since the openssl version of
// CreateSelfSignedCert is not implemented. We should mock out this dependency
// and remove the if-defined.
-
#if !defined(OS_ANDROID)
+
+static const char* kFakeOrigin = "http://foo.com";
+static const char* kFakeIdentityName1 = "name1";
+static const char* kFakeIdentityName2 = "name2";
+static const char* kFakeCommonName1 = "cname1";
+static const char* kFakeCommonName2 = "cname2";
+
+static void OnRequestCompleted(bool* completed,
+ std::string* out_cert,
+ std::string* out_key,
+ int error,
+ const std::string& certificate,
+ const std::string& private_key) {
+ ASSERT_EQ(net::OK, error);
+ ASSERT_NE("", certificate);
+ ASSERT_NE("", private_key);
+ *completed = true;
+ *out_cert = certificate;
+ *out_key = private_key;
+}
+
class WebRTCIdentityStoreTest : public testing::Test {
public:
WebRTCIdentityStoreTest()
- : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
+ : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP |
+ TestBrowserThreadBundle::REAL_DB_THREAD),
pool_owner_(
new base::SequencedWorkerPoolOwner(3, "WebRTCIdentityStoreTest")),
- webrtc_identity_store_(new WebRTCIdentityStore()) {
+ webrtc_identity_store_(
+ new WebRTCIdentityStore(base::FilePath(), NULL)) {
webrtc_identity_store_->SetTaskRunnerForTesting(pool_owner_->pool());
}
@@ -33,74 +55,224 @@ class WebRTCIdentityStoreTest : public testing::Test {
pool_owner_->pool()->Shutdown();
}
+ void RunUntilIdle() {
+ RunAllPendingInMessageLoop(BrowserThread::DB);
+ RunAllPendingInMessageLoop(BrowserThread::IO);
+ pool_owner_->pool()->FlushForTesting();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ base::Closure RequestIdentityAndRunUtilIdle(const std::string& origin,
+ const std::string& identity_name,
+ const std::string& common_name,
+ bool* completed,
+ std::string* certificate,
+ std::string* private_key) {
+ base::Closure cancel_callback = webrtc_identity_store_->RequestIdentity(
+ GURL(origin),
+ identity_name,
+ common_name,
+ base::Bind(&OnRequestCompleted, completed, certificate, private_key));
+ EXPECT_FALSE(cancel_callback.is_null());
+ RunUntilIdle();
+ return cancel_callback;
+ }
+
protected:
TestBrowserThreadBundle browser_thread_bundle_;
scoped_ptr<base::SequencedWorkerPoolOwner> pool_owner_;
- scoped_ptr<WebRTCIdentityStore> webrtc_identity_store_;
+ scoped_refptr<WebRTCIdentityStore> webrtc_identity_store_;
};
-static void OnRequestCompleted(bool* completed,
- int error,
- const std::string& certificate,
- const std::string& private_key) {
- ASSERT_EQ(net::OK, error);
- ASSERT_NE("", certificate);
- ASSERT_NE("", private_key);
- *completed = true;
-}
-
TEST_F(WebRTCIdentityStoreTest, RequestIdentity) {
bool completed = false;
+ std::string dummy;
base::Closure cancel_callback =
- webrtc_identity_store_->RequestIdentity(
- GURL("http://google.com"),
- "a",
- "b",
- base::Bind(&OnRequestCompleted, &completed));
- ASSERT_FALSE(cancel_callback.is_null());
- pool_owner_->pool()->FlushForTesting();
- base::RunLoop().RunUntilIdle();
+ RequestIdentityAndRunUtilIdle(kFakeOrigin,
+ kFakeIdentityName1,
+ kFakeCommonName1,
+ &completed,
+ &dummy,
+ &dummy);
EXPECT_TRUE(completed);
}
TEST_F(WebRTCIdentityStoreTest, CancelRequest) {
bool completed = false;
- base::Closure cancel_callback =
- webrtc_identity_store_->RequestIdentity(
- GURL("http://google.com"),
- "a",
- "b",
- base::Bind(&OnRequestCompleted, &completed));
+ std::string dummy;
+ base::Closure cancel_callback = webrtc_identity_store_->RequestIdentity(
+ GURL(kFakeOrigin),
+ kFakeIdentityName1,
+ kFakeCommonName1,
+ base::Bind(&OnRequestCompleted, &completed, &dummy, &dummy));
ASSERT_FALSE(cancel_callback.is_null());
cancel_callback.Run();
- pool_owner_->pool()->FlushForTesting();
- base::RunLoop().RunUntilIdle();
+
+ RunUntilIdle();
EXPECT_FALSE(completed);
}
-TEST_F(WebRTCIdentityStoreTest, MultipleRequests) {
+TEST_F(WebRTCIdentityStoreTest, ConcurrentUniqueRequests) {
bool completed_1 = false;
bool completed_2 = false;
- base::Closure cancel_callback_1 =
- webrtc_identity_store_->RequestIdentity(
- GURL("http://foo.com"),
- "a",
- "b",
- base::Bind(&OnRequestCompleted, &completed_1));
+ std::string dummy;
+ base::Closure cancel_callback_1 = webrtc_identity_store_->RequestIdentity(
+ GURL(kFakeOrigin),
+ kFakeIdentityName1,
+ kFakeCommonName1,
+ base::Bind(&OnRequestCompleted, &completed_1, &dummy, &dummy));
ASSERT_FALSE(cancel_callback_1.is_null());
+ base::Closure cancel_callback_2 = webrtc_identity_store_->RequestIdentity(
+ GURL(kFakeOrigin),
+ kFakeIdentityName2,
+ kFakeCommonName1,
+ base::Bind(&OnRequestCompleted, &completed_2, &dummy, &dummy));
+ ASSERT_FALSE(cancel_callback_2.is_null());
+
+ RunUntilIdle();
+ EXPECT_TRUE(completed_1);
+ EXPECT_TRUE(completed_2);
+}
+
+TEST_F(WebRTCIdentityStoreTest, DifferentCommonNameReturnNewIdentity) {
+ bool completed_1 = false;
+ bool completed_2 = false;
+ std::string cert_1, cert_2, key_1, key_2;
+
+ base::Closure cancel_callback_1 =
+ RequestIdentityAndRunUtilIdle(kFakeOrigin,
+ kFakeIdentityName1,
+ kFakeCommonName1,
+ &completed_1,
+ &cert_1,
+ &key_1);
+
base::Closure cancel_callback_2 =
- webrtc_identity_store_->RequestIdentity(
- GURL("http://bar.com"),
- "a",
- "b",
- base::Bind(&OnRequestCompleted, &completed_2));
+ RequestIdentityAndRunUtilIdle(kFakeOrigin,
+ kFakeIdentityName1,
+ kFakeCommonName2,
+ &completed_2,
+ &cert_2,
+ &key_2);
+
+ EXPECT_TRUE(completed_1);
+ EXPECT_TRUE(completed_2);
+ EXPECT_NE(cert_1, cert_2);
+ EXPECT_NE(key_1, key_2);
+}
+
+TEST_F(WebRTCIdentityStoreTest, SerialIdenticalRequests) {
+ bool completed_1 = false;
+ bool completed_2 = false;
+ std::string cert_1, cert_2, key_1, key_2;
+
+ base::Closure cancel_callback_1 =
+ RequestIdentityAndRunUtilIdle(kFakeOrigin,
+ kFakeIdentityName1,
+ kFakeCommonName1,
+ &completed_1,
+ &cert_1,
+ &key_1);
+
+ base::Closure cancel_callback_2 =
+ RequestIdentityAndRunUtilIdle(kFakeOrigin,
+ kFakeIdentityName1,
+ kFakeCommonName1,
+ &completed_2,
+ &cert_2,
+ &key_2);
+
+ EXPECT_TRUE(completed_1);
+ EXPECT_TRUE(completed_2);
+ EXPECT_EQ(cert_1, cert_2);
+ EXPECT_EQ(key_1, key_2);
+}
+
+TEST_F(WebRTCIdentityStoreTest, ConcurrentIdenticalRequestsJoined) {
+ bool completed_1 = false;
+ bool completed_2 = false;
+ std::string cert_1, cert_2, key_1, key_2;
+
+ base::Closure cancel_callback_1 = webrtc_identity_store_->RequestIdentity(
+ GURL(kFakeOrigin),
+ kFakeIdentityName1,
+ kFakeCommonName1,
+ base::Bind(&OnRequestCompleted, &completed_1, &cert_1, &key_1));
+ ASSERT_FALSE(cancel_callback_1.is_null());
+
+ base::Closure cancel_callback_2 = webrtc_identity_store_->RequestIdentity(
+ GURL(kFakeOrigin),
+ kFakeIdentityName1,
+ kFakeCommonName1,
+ base::Bind(&OnRequestCompleted, &completed_2, &cert_2, &key_2));
+ ASSERT_FALSE(cancel_callback_2.is_null());
+
+ RunUntilIdle();
+ EXPECT_TRUE(completed_1);
+ EXPECT_TRUE(completed_2);
+ EXPECT_EQ(cert_1, cert_2);
+ EXPECT_EQ(key_1, key_2);
+}
+
+TEST_F(WebRTCIdentityStoreTest, CancelOneOfIdenticalRequests) {
+ bool completed_1 = false;
+ bool completed_2 = false;
+ std::string cert_1, cert_2, key_1, key_2;
+
+ base::Closure cancel_callback_1 = webrtc_identity_store_->RequestIdentity(
+ GURL(kFakeOrigin),
+ kFakeIdentityName1,
+ kFakeCommonName1,
+ base::Bind(&OnRequestCompleted, &completed_1, &cert_1, &key_1));
+ ASSERT_FALSE(cancel_callback_1.is_null());
+
+ base::Closure cancel_callback_2 = webrtc_identity_store_->RequestIdentity(
+ GURL(kFakeOrigin),
+ kFakeIdentityName1,
+ kFakeCommonName1,
+ base::Bind(&OnRequestCompleted, &completed_2, &cert_2, &key_2));
ASSERT_FALSE(cancel_callback_2.is_null());
- pool_owner_->pool()->FlushForTesting();
- base::RunLoop().RunUntilIdle();
+ cancel_callback_1.Run();
+
+ RunUntilIdle();
+ EXPECT_FALSE(completed_1);
+ EXPECT_TRUE(completed_2);
+}
+
+TEST_F(WebRTCIdentityStoreTest, DeleteDataAndGenerateNewIdentity) {
+ bool completed_1 = false;
+ bool completed_2 = false;
+ std::string cert_1, cert_2, key_1, key_2;
+
+ // Generate the first identity.
+ base::Closure cancel_callback_1 =
+ RequestIdentityAndRunUtilIdle(kFakeOrigin,
+ kFakeIdentityName1,
+ kFakeCommonName1,
+ &completed_1,
+ &cert_1,
+ &key_1);
+
+ // Clear the data and the second request should return a new identity.
+ webrtc_identity_store_->DeleteBetween(
+ base::Time(), base::Time::Now(), base::Bind(&base::DoNothing));
+ RunUntilIdle();
+
+ base::Closure cancel_callback_2 =
+ RequestIdentityAndRunUtilIdle(kFakeOrigin,
+ kFakeIdentityName1,
+ kFakeCommonName1,
+ &completed_2,
+ &cert_2,
+ &key_2);
+
EXPECT_TRUE(completed_1);
EXPECT_TRUE(completed_2);
+ EXPECT_NE(cert_1, cert_2);
+ EXPECT_NE(key_1, key_2);
}
+
#endif
} // namespace content
diff --git a/content/browser/renderer_host/media/webrtc_identity_service_host.cc b/content/browser/renderer_host/media/webrtc_identity_service_host.cc
index cce3c74..0230b26 100644
--- a/content/browser/renderer_host/media/webrtc_identity_service_host.cc
+++ b/content/browser/renderer_host/media/webrtc_identity_service_host.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
+#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/media/webrtc_identity_store.h"
#include "content/common/media/webrtc_identity_messages.h"
#include "net/base/net_errors.h"
@@ -13,8 +14,10 @@
namespace content {
WebRTCIdentityServiceHost::WebRTCIdentityServiceHost(
+ int renderer_process_id,
WebRTCIdentityStore* identity_store)
- : identity_store_(identity_store) {}
+ : renderer_process_id_(renderer_process_id),
+ identity_store_(identity_store) {}
WebRTCIdentityServiceHost::~WebRTCIdentityServiceHost() {
if (!cancel_callback_.is_null())
@@ -42,6 +45,15 @@ void WebRTCIdentityServiceHost::OnRequestIdentity(
SendErrorMessage(net::ERR_INSUFFICIENT_RESOURCES);
return;
}
+
+ ChildProcessSecurityPolicyImpl* policy =
+ ChildProcessSecurityPolicyImpl::GetInstance();
+ if (!policy->CanAccessCookiesForOrigin(renderer_process_id_, origin)) {
+ DLOG(WARNING) << "Request rejected because origin access is denied.";
+ SendErrorMessage(net::ERR_ACCESS_DENIED);
+ return;
+ }
+
cancel_callback_ = identity_store_->RequestIdentity(
origin,
identity_name,
diff --git a/content/browser/renderer_host/media/webrtc_identity_service_host.h b/content/browser/renderer_host/media/webrtc_identity_service_host.h
index 4c53056..3676223 100644
--- a/content/browser/renderer_host/media/webrtc_identity_service_host.h
+++ b/content/browser/renderer_host/media/webrtc_identity_service_host.h
@@ -26,7 +26,8 @@ class WebRTCIdentityStore;
// ERR_INSUFFICIENT_RESOURCES will be sent back to the renderer.
class CONTENT_EXPORT WebRTCIdentityServiceHost : public BrowserMessageFilter {
public:
- explicit WebRTCIdentityServiceHost(WebRTCIdentityStore* identity_store);
+ explicit WebRTCIdentityServiceHost(int renderer_process_id,
+ WebRTCIdentityStore* identity_store);
protected:
virtual ~WebRTCIdentityServiceHost();
@@ -50,6 +51,7 @@ class CONTENT_EXPORT WebRTCIdentityServiceHost : public BrowserMessageFilter {
void SendErrorMessage(int error);
+ int renderer_process_id_;
base::Closure cancel_callback_;
WebRTCIdentityStore* identity_store_;
diff --git a/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc b/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc
index 058e15be..341378d 100644
--- a/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc
+++ b/content/browser/renderer_host/media/webrtc_identity_service_host_unittest.cc
@@ -4,9 +4,11 @@
#include <deque>
+#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/media/webrtc_identity_store.h"
#include "content/browser/renderer_host/media/webrtc_identity_service_host.h"
#include "content/common/media/webrtc_identity_messages.h"
+#include "content/public/test/test_browser_thread_bundle.h"
#include "ipc/ipc_message.h"
#include "net/base/net_errors.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -21,9 +23,12 @@ static const char FAKE_COMMON_NAME[] = "fake common name";
static const char FAKE_CERTIFICATE[] = "fake cert";
static const char FAKE_PRIVATE_KEY[] = "fake private key";
static const int FAKE_ERROR = 100;
+static const int FAKE_RENDERER_ID = 10;
class MockWebRTCIdentityStore : public WebRTCIdentityStore {
public:
+ MockWebRTCIdentityStore() : WebRTCIdentityStore(base::FilePath(), NULL) {}
+
virtual base::Closure RequestIdentity(
const GURL& origin,
const std::string& identity_name,
@@ -46,6 +51,8 @@ class MockWebRTCIdentityStore : public WebRTCIdentityStore {
}
private:
+ virtual ~MockWebRTCIdentityStore() {}
+
void OnCancel() { callback_.Reset(); }
CompletionCallback callback_;
@@ -54,7 +61,11 @@ class MockWebRTCIdentityStore : public WebRTCIdentityStore {
class WebRTCIdentityServiceHostForTest : public WebRTCIdentityServiceHost {
public:
explicit WebRTCIdentityServiceHostForTest(WebRTCIdentityStore* identity_store)
- : WebRTCIdentityServiceHost(identity_store) {}
+ : WebRTCIdentityServiceHost(FAKE_RENDERER_ID, identity_store) {
+ ChildProcessSecurityPolicyImpl* policy =
+ ChildProcessSecurityPolicyImpl::GetInstance();
+ policy->Add(FAKE_RENDERER_ID);
+ }
virtual bool Send(IPC::Message* message) OVERRIDE {
messages_.push_back(*message);
@@ -75,7 +86,11 @@ class WebRTCIdentityServiceHostForTest : public WebRTCIdentityServiceHost {
void ClearMessages() { messages_.clear(); }
private:
- virtual ~WebRTCIdentityServiceHostForTest() {}
+ virtual ~WebRTCIdentityServiceHostForTest() {
+ ChildProcessSecurityPolicyImpl* policy =
+ ChildProcessSecurityPolicyImpl::GetInstance();
+ policy->Remove(FAKE_RENDERER_ID);
+ }
std::deque<IPC::Message> messages_;
};
@@ -83,7 +98,8 @@ class WebRTCIdentityServiceHostForTest : public WebRTCIdentityServiceHost {
class WebRTCIdentityServiceHostTest : public ::testing::Test {
public:
WebRTCIdentityServiceHostTest()
- : store_(new MockWebRTCIdentityStore()),
+ : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
+ store_(new MockWebRTCIdentityStore()),
host_(new WebRTCIdentityServiceHostForTest(store_.get())) {}
void SendRequestToHost() {
@@ -124,7 +140,8 @@ class WebRTCIdentityServiceHostTest : public ::testing::Test {
}
protected:
- scoped_ptr<MockWebRTCIdentityStore> store_;
+ TestBrowserThreadBundle browser_thread_bundle_;
+ scoped_refptr<MockWebRTCIdentityStore> store_;
scoped_refptr<WebRTCIdentityServiceHostForTest> host_;
};
@@ -158,4 +175,13 @@ TEST_F(WebRTCIdentityServiceHostTest, TestOnRequestFailed) {
VerifyRequestFailedMessage(net::ERR_KEY_GENERATION_FAILED);
}
+TEST_F(WebRTCIdentityServiceHostTest, TestOriginAccessDenied) {
+ ChildProcessSecurityPolicyImpl* policy =
+ ChildProcessSecurityPolicyImpl::GetInstance();
+ policy->Remove(FAKE_RENDERER_ID);
+
+ SendRequestToHost();
+ VerifyRequestFailedMessage(net::ERR_ACCESS_DENIED);
+}
+
} // namespace content
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index d3ba06e..3ed4860 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -652,7 +652,7 @@ void RenderProcessHostImpl::CreateMessageFilters() {
channel_->AddFilter(gpu_message_filter_);
#if defined(ENABLE_WEBRTC)
channel_->AddFilter(new WebRTCIdentityServiceHost(
- storage_partition_impl_->GetWebRTCIdentityStore()));
+ GetID(), storage_partition_impl_->GetWebRTCIdentityStore()));
peer_connection_tracker_host_ = new PeerConnectionTrackerHost(GetID());
channel_->AddFilter(peer_connection_tracker_host_.get());
channel_->AddFilter(new MediaStreamDispatcherHost(
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 1775b5f..361b4d1 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -252,6 +252,7 @@ struct StoragePartitionImpl::DataDeletionHelper {
net::URLRequestContextGetter* rq_context,
DOMStorageContextWrapper* dom_storage_context,
quota::QuotaManager* quota_manager,
+ WebRTCIdentityStore* webrtc_identity_store,
const base::Time begin,
const base::Time end);
@@ -284,7 +285,7 @@ StoragePartitionImpl::StoragePartitionImpl(
webkit_database::DatabaseTracker* database_tracker,
DOMStorageContextWrapper* dom_storage_context,
IndexedDBContextImpl* indexed_db_context,
- scoped_ptr<WebRTCIdentityStore> webrtc_identity_store)
+ WebRTCIdentityStore* webrtc_identity_store)
: partition_path_(partition_path),
quota_manager_(quota_manager),
appcache_service_(appcache_service),
@@ -292,7 +293,7 @@ StoragePartitionImpl::StoragePartitionImpl(
database_tracker_(database_tracker),
dom_storage_context_(dom_storage_context),
indexed_db_context_(indexed_db_context),
- webrtc_identity_store_(webrtc_identity_store.Pass()) {}
+ webrtc_identity_store_(webrtc_identity_store) {}
StoragePartitionImpl::~StoragePartitionImpl() {
// These message loop checks are just to avoid leaks in unittests.
@@ -367,8 +368,8 @@ StoragePartitionImpl* StoragePartitionImpl::Create(
scoped_refptr<ChromeAppCacheService> appcache_service =
new ChromeAppCacheService(quota_manager->proxy());
- scoped_ptr<WebRTCIdentityStore> webrtc_identity_store(
- new WebRTCIdentityStore());
+ scoped_refptr<WebRTCIdentityStore> webrtc_identity_store(
+ new WebRTCIdentityStore(path, context->GetSpecialStoragePolicy()));
return new StoragePartitionImpl(partition_path,
quota_manager.get(),
@@ -377,7 +378,7 @@ StoragePartitionImpl* StoragePartitionImpl::Create(
database_tracker.get(),
dom_storage_context.get(),
indexed_db_context.get(),
- webrtc_identity_store.Pass());
+ webrtc_identity_store.get());
}
base::FilePath StoragePartitionImpl::GetPath() {
@@ -431,7 +432,8 @@ void StoragePartitionImpl::ClearDataImpl(
// DataDeletionHelper::DecrementTaskCountOnUI().
helper->ClearDataOnUIThread(
remove_mask, quota_storage_remove_mask, remove_origin,
- GetPath(), rq_context, dom_storage_context_, quota_manager_, begin, end);
+ GetPath(), rq_context, dom_storage_context_, quota_manager_,
+ webrtc_identity_store_, begin, end);
}
void StoragePartitionImpl::
@@ -546,6 +548,7 @@ void StoragePartitionImpl::DataDeletionHelper::ClearDataOnUIThread(
net::URLRequestContextGetter* rq_context,
DOMStorageContextWrapper* dom_storage_context,
quota::QuotaManager* quota_manager,
+ WebRTCIdentityStore* webrtc_identity_store,
const base::Time begin,
const base::Time end) {
DCHECK_NE(remove_mask, 0u);
@@ -602,6 +605,18 @@ void StoragePartitionImpl::DataDeletionHelper::ClearDataOnUIThread(
path, begin, end, decrement_callback));
}
+ if (remove_mask & REMOVE_DATA_MASK_WEBRTC_IDENTITY) {
+ IncrementTaskCountOnUI();
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&WebRTCIdentityStore::DeleteBetween,
+ webrtc_identity_store,
+ begin,
+ end,
+ decrement_callback));
+ }
+
DecrementTaskCountOnUI();
}
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h
index 8665a74..e59347c 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -77,7 +77,7 @@ class StoragePartitionImpl : public StoragePartition {
webkit_database::DatabaseTracker* database_tracker,
DOMStorageContextWrapper* dom_storage_context,
IndexedDBContextImpl* indexed_db_context,
- scoped_ptr<WebRTCIdentityStore> webrtc_identity_store);
+ WebRTCIdentityStore* webrtc_identity_store);
void ClearDataImpl(uint32 remove_mask,
uint32 quota_storage_remove_mask,
@@ -112,7 +112,7 @@ class StoragePartitionImpl : public StoragePartition {
scoped_refptr<webkit_database::DatabaseTracker> database_tracker_;
scoped_refptr<DOMStorageContextWrapper> dom_storage_context_;
scoped_refptr<IndexedDBContextImpl> indexed_db_context_;
- scoped_ptr<WebRTCIdentityStore> webrtc_identity_store_;
+ scoped_refptr<WebRTCIdentityStore> webrtc_identity_store_;
DISALLOW_COPY_AND_ASSIGN(StoragePartitionImpl);
};
diff --git a/content/browser/storage_partition_impl_unittest.cc b/content/browser/storage_partition_impl_unittest.cc
index 2207837..a8b47bc 100644
--- a/content/browser/storage_partition_impl_unittest.cc
+++ b/content/browser/storage_partition_impl_unittest.cc
@@ -109,14 +109,8 @@ TEST_F(StoragePartitionShaderClearTest, ClearShaderCache) {
EXPECT_EQ(1u, Size());
TestClosureCallback clear_cb;
- StoragePartitionImpl sp(cache_path(),
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- scoped_ptr<WebRTCIdentityStore>());
+ StoragePartitionImpl sp(
+ cache_path(), NULL, NULL, NULL, NULL, NULL, NULL, NULL);
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&ClearData, &sp, clear_cb.callback()));
clear_cb.WaitForResult();
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index b3751ac..0fc5e68 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -707,6 +707,8 @@
'browser/media/media_internals_ui.h',
'browser/media/webrtc_identity_store.cc',
'browser/media/webrtc_identity_store.h',
+ 'browser/media/webrtc_identity_store_backend.cc',
+ 'browser/media/webrtc_identity_store_backend.h',
'browser/media/webrtc_internals.cc',
'browser/media/webrtc_internals.h',
'browser/media/webrtc_internals_message_handler.cc',
diff --git a/content/public/browser/storage_partition.h b/content/public/browser/storage_partition.h
index f69feaa..b30ca42 100644
--- a/content/public/browser/storage_partition.h
+++ b/content/public/browser/storage_partition.h
@@ -64,7 +64,7 @@ class StoragePartition {
REMOVE_DATA_MASK_LOCAL_STORAGE = 1 << 4,
REMOVE_DATA_MASK_SHADER_CACHE = 1 << 5,
REMOVE_DATA_MASK_WEBSQL = 1 << 6,
-
+ REMOVE_DATA_MASK_WEBRTC_IDENTITY = 1 << 7,
REMOVE_DATA_MASK_ALL = -1
};