Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(196)

Unified Diff: content/browser/browser_context.cc

Issue 10600009: Support partitioning of storage contexts based on render_id. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Merge in unittest fix from jam@ Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/browser_context.cc
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc
index 522ccd108edc29658a73f380f2713a33e3b10a37..759dc1cbec259ea7fac60d3dfa27c24bce8584c2 100644
--- a/content/browser/browser_context.cc
+++ b/content/browser/browser_context.cc
@@ -4,6 +4,8 @@
#include "content/public/browser/browser_context.h"
+#include "base/file_path.h"
+#include "base/stl_util.h"
#include "content/browser/appcache/chrome_appcache_service.h"
#include "content/browser/dom_storage/dom_storage_context_impl.h"
#include "content/browser/download/download_file_manager.h"
@@ -12,6 +14,7 @@
#include "content/browser/in_process_webkit/indexed_db_context_impl.h"
#include "content/browser/renderer_host/resource_dispatcher_host_impl.h"
#include "content/browser/resource_context_impl.h"
+#include "content/common/child_process_host_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_constants.h"
@@ -31,89 +34,252 @@ using quota::QuotaManager;
using webkit_database::DatabaseTracker;
// Key names on BrowserContext.
-static const char* kAppCacheServicKeyName = "content_appcache_service_tracker";
-static const char* kDatabaseTrackerKeyName = "content_database_tracker";
-static const char* kDOMStorageContextKeyName = "content_dom_storage_context";
static const char* kDownloadManagerKeyName = "download_manager";
-static const char* kFileSystemContextKeyName = "content_file_system_context";
-static const char* kIndexedDBContextKeyName = "content_indexed_db_context";
-static const char* kQuotaManagerKeyName = "content_quota_manager";
+static const char* kStorageParitionMapKeyName = "content_storage_partition_map";
+
+// Dirname for storing persistent data for renderers with isolated storage.
+const FilePath::CharType kStoragePartitionDirName[] =
+ FILE_PATH_LITERAL("Storage Partitions");
namespace content {
namespace {
-void CreateQuotaManagerAndClients(BrowserContext* context) {
- if (context->GetUserData(kQuotaManagerKeyName)) {
- DCHECK(context->GetUserData(kDatabaseTrackerKeyName));
- DCHECK(context->GetUserData(kDOMStorageContextKeyName));
- DCHECK(context->GetUserData(kFileSystemContextKeyName));
- DCHECK(context->GetUserData(kIndexedDBContextKeyName));
- return;
+// Defines the what persistent state a child process can access.
+//
+// The StoragePartition defines the view each child process has of the
+// persistent state inside the BrowserContext. This is used to implement
+// isolated storage where a renderer with isolated storage cannot see
+// the cookies, localStorage, etc., that normal web renderers have access to.
+class StoragePartition {
+ public:
+ ~StoragePartition() {
+ // These message loop checks are just to avoid leaks in unittests.
+ if (database_tracker() &&
+ BrowserThread::IsMessageLoopValid(BrowserThread::FILE)) {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&webkit_database::DatabaseTracker::Shutdown,
+ database_tracker()));
+ }
+
+ if (dom_storage_context())
+ dom_storage_context()->Shutdown();
}
- // All of the clients have to be created and registered with the
- // QuotaManager prior to the QuotaManger being used. So we do them
- // all together here prior to handing out a reference to anything
- // that utlizes the QuotaManager.
- scoped_refptr<QuotaManager> quota_manager = new quota::QuotaManager(
- context->IsOffTheRecord(), context->GetPath(),
- BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
- BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB),
- context->GetSpecialStoragePolicy());
- context->SetUserData(kQuotaManagerKeyName,
- new UserDataAdapter<QuotaManager>(quota_manager));
-
- // Each consumer is responsible for registering its QuotaClient during
- // its construction.
- scoped_refptr<FileSystemContext> filesystem_context = CreateFileSystemContext(
- context->GetPath(), context->IsOffTheRecord(),
- context->GetSpecialStoragePolicy(), quota_manager->proxy());
- context->SetUserData(
- kFileSystemContextKeyName,
- new UserDataAdapter<FileSystemContext>(filesystem_context));
-
- scoped_refptr<DatabaseTracker> db_tracker = new DatabaseTracker(
- context->GetPath(), context->IsOffTheRecord(),
- context->GetSpecialStoragePolicy(), quota_manager->proxy(),
- BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
- context->SetUserData(kDatabaseTrackerKeyName,
- new UserDataAdapter<DatabaseTracker>(db_tracker));
-
- FilePath path = context->IsOffTheRecord() ? FilePath() : context->GetPath();
- scoped_refptr<DOMStorageContextImpl> dom_storage_context =
- new DOMStorageContextImpl(path, context->GetSpecialStoragePolicy());
- context->SetUserData(
- kDOMStorageContextKeyName,
- new UserDataAdapter<DOMStorageContextImpl>(dom_storage_context));
-
- scoped_refptr<IndexedDBContext> indexed_db_context = new IndexedDBContextImpl(
- path, context->GetSpecialStoragePolicy(), quota_manager->proxy(),
- BrowserThread::GetMessageLoopProxyForThread(
- BrowserThread::WEBKIT_DEPRECATED));
- context->SetUserData(
- kIndexedDBContextKeyName,
- new UserDataAdapter<IndexedDBContext>(indexed_db_context));
-
- scoped_refptr<ChromeAppCacheService> appcache_service =
- new ChromeAppCacheService(quota_manager->proxy());
- context->SetUserData(
- kAppCacheServicKeyName,
- new UserDataAdapter<ChromeAppCacheService>(appcache_service));
-
- InitializeResourceContext(context);
-
- // Check first to avoid memory leak in unittests.
- if (BrowserThread::IsMessageLoopValid(BrowserThread::IO)) {
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&ChromeAppCacheService::InitializeOnIOThread,
- appcache_service,
- context->IsOffTheRecord() ? FilePath() :
- context->GetPath().Append(content::kAppCacheDirname),
- context->GetResourceContext(),
- make_scoped_refptr(context->GetSpecialStoragePolicy())));
+ // TODO(ajwong): Break the direct dependency on |context|. We only
+ // need 3 pieces of info from it.
+ static StoragePartition* Create(BrowserContext* context,
+ FilePath partition_path) {
+ // All of the clients have to be created and registered with the
+ // QuotaManager prior to the QuotaManger being used. We do them
+ // all together here prior to handing out a reference to anything
+ // that utilizes the QuotaManager.
+ scoped_refptr<QuotaManager> quota_manager = new quota::QuotaManager(
+ context->IsOffTheRecord(), partition_path,
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB),
+ context->GetSpecialStoragePolicy());
+
+ // Each consumer is responsible for registering its QuotaClient during
+ // its construction.
+ scoped_refptr<FileSystemContext> filesystem_context =
+ CreateFileSystemContext(partition_path, context->IsOffTheRecord(),
+ context->GetSpecialStoragePolicy(),
+ quota_manager->proxy());
+
+ scoped_refptr<DatabaseTracker> database_tracker = new DatabaseTracker(
+ partition_path, context->IsOffTheRecord(),
+ context->GetSpecialStoragePolicy(), quota_manager->proxy(),
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
+
+ FilePath path = context->IsOffTheRecord() ? FilePath() : partition_path;
+ scoped_refptr<DOMStorageContextImpl> dom_storage_context =
+ new DOMStorageContextImpl(path, context->GetSpecialStoragePolicy());
+
+ scoped_refptr<IndexedDBContextImpl> indexed_db_context =
+ new IndexedDBContextImpl(path, context->GetSpecialStoragePolicy(),
+ quota_manager->proxy(),
+ BrowserThread::GetMessageLoopProxyForThread(
+ BrowserThread::WEBKIT_DEPRECATED));
+
+ scoped_refptr<ChromeAppCacheService> appcache_service =
+ new ChromeAppCacheService(quota_manager->proxy());
+
+ return new StoragePartition(partition_path,
+ quota_manager,
+ appcache_service,
+ filesystem_context,
+ database_tracker,
+ dom_storage_context,
+ indexed_db_context);
+ }
+
+ quota::QuotaManager* quota_manager() {
+ return quota_manager_;
+ }
+ ChromeAppCacheService* appcache_service() {
+ return appcache_service_;
+ }
+ fileapi::FileSystemContext* filesystem_context() {
+ return filesystem_context_;
+ }
+ webkit_database::DatabaseTracker* database_tracker() {
+ return database_tracker_;
+ }
+ DOMStorageContextImpl* dom_storage_context() {
+ return dom_storage_context_;
+ }
+ IndexedDBContext* indexed_db_context() {
+ return indexed_db_context_;
+ }
+
+ private:
+ StoragePartition(const FilePath& partition_path,
+ quota::QuotaManager* quota_manager,
+ ChromeAppCacheService* appcache_service,
+ fileapi::FileSystemContext* filesystem_context,
+ webkit_database::DatabaseTracker* database_tracker,
+ DOMStorageContextImpl* dom_storage_context,
+ IndexedDBContext* indexed_db_context)
+ : partition_path_(partition_path),
+ quota_manager_(quota_manager),
+ appcache_service_(appcache_service),
+ filesystem_context_(filesystem_context),
+ database_tracker_(database_tracker),
+ dom_storage_context_(dom_storage_context),
+ indexed_db_context_(indexed_db_context) {
+ }
+
+ FilePath partition_path_;
+ scoped_refptr<quota::QuotaManager> quota_manager_;
+ scoped_refptr<ChromeAppCacheService> appcache_service_;
+ scoped_refptr<fileapi::FileSystemContext> filesystem_context_;
+ scoped_refptr<webkit_database::DatabaseTracker> database_tracker_;
+ scoped_refptr<DOMStorageContextImpl> dom_storage_context_;
+ scoped_refptr<IndexedDBContext> indexed_db_context_;
+};
+
+// A std::string to StoragePartition map for use with SupportsUserData APIs.
+class StoragePartitionMap : public base::SupportsUserData::Data {
+ public:
+ explicit StoragePartitionMap(BrowserContext* browser_context)
+ : browser_context_(browser_context) {
+ }
+
+ virtual ~StoragePartitionMap() {
+ STLDeleteContainerPairSecondPointers(partitions_.begin(),
+ partitions_.end());
+ }
+
+ // This map retains ownership of the returned StoragePartition objects.
+ StoragePartition* Get(const std::string& partition_id) {
+ // Find the previously created partition if it's available.
+ std::map<std::string, StoragePartition*>::const_iterator it =
+ partitions_.find(partition_id);
+ if (it != partitions_.end())
+ return it->second;
+
+ // There was no previous partition, so let's make a new one.
+ FilePath partition_path = browser_context_->GetPath();
+ if (!partition_id.empty()) {
+ CHECK(IsStringASCII(partition_id));
+ partition_path = partition_path.Append(kStoragePartitionDirName)
+ .AppendASCII(partition_id);
+ }
+
+ StoragePartition* storage_partition =
+ StoragePartition::Create(browser_context_, partition_path);
+ partitions_[partition_id] = storage_partition;
+
+ PostCreateInitialization(storage_partition, partition_path);
+
+ // TODO(ajwong): We need to remove this conditional by making
+ // InitializeResourceContext() understand having different partition data
+ // based on the renderer_id.
+ if (partition_id.empty()) {
+ InitializeResourceContext(browser_context_);
+ }
+
+ return storage_partition;
+ }
+
+ void ForEach(const base::Callback<void(StoragePartition*)>& callback) {
+ for (std::map<std::string, StoragePartition*>::const_iterator it =
+ partitions_.begin();
+ it != partitions_.end();
+ ++it) {
+ callback.Run(it->second);
+ }
+ }
+
+ private:
+ // TODO(ajwong): This must always be called *after* it's been added to the
+ // partition map. This feels dangerous. Should this not be in this class?
+ void PostCreateInitialization(StoragePartition* partition,
+ const FilePath& partition_path) {
+ // Check first to avoid memory leak in unittests.
+ if (BrowserThread::IsMessageLoopValid(BrowserThread::IO)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&ChromeAppCacheService::InitializeOnIOThread,
+ partition->appcache_service(),
+ browser_context_->IsOffTheRecord() ? FilePath() :
+ partition_path.Append(content::kAppCacheDirname),
+ browser_context_->GetResourceContext(),
+ make_scoped_refptr(
+ browser_context_->GetSpecialStoragePolicy())));
+ }
}
+
+ BrowserContext* browser_context_;
+ std::map<std::string, StoragePartition*> partitions_;
+};
+
+StoragePartition* GetStoragePartition(BrowserContext* browser_context,
+ int renderer_child_id) {
+ StoragePartitionMap* partition_map = static_cast<StoragePartitionMap*>(
+ browser_context->GetUserData(kStorageParitionMapKeyName));
+ if (!partition_map) {
+ partition_map = new StoragePartitionMap(browser_context);
+ browser_context->SetUserData(kStorageParitionMapKeyName, partition_map);
+ }
+
+ const std::string& partition_id =
+ GetContentClient()->browser()->GetStoragePartitionIdForChildProcess(
+ browser_context,
+ renderer_child_id);
+
+ return partition_map->Get(partition_id);
+}
+
+// Run |callback| on each storage partition in |browser_context|.
+void ForEachStoragePartition(
+ BrowserContext* browser_context,
+ const base::Callback<void(StoragePartition*)>& callback) {
+ StoragePartitionMap* partition_map = static_cast<StoragePartitionMap*>(
+ browser_context->GetUserData(kStorageParitionMapKeyName));
+ if (!partition_map) {
+ return;
+ }
+
+ partition_map->ForEach(callback);
+}
+
+// Used to convert a callback meant to take a DOMStorageContextImpl* into one
+// that can take a StoragePartition*.
+void ProcessDOMStorageContext(
+ const base::Callback<void(DOMStorageContextImpl*)>& callback,
+ StoragePartition* partition) {
+ callback.Run(partition->dom_storage_context());
+}
+
+// Run |callback| on each DOMStorageContextImpl in |browser_context|.
+void ForEachDOMStorageContext(
+ BrowserContext* browser_context,
+ const base::Callback<void(DOMStorageContextImpl*)>& callback) {
+ ForEachStoragePartition(browser_context,
+ base::Bind(&ProcessDOMStorageContext, callback));
}
void SaveSessionStateOnIOThread(ResourceContext* resource_context) {
@@ -134,9 +300,10 @@ void PurgeMemoryOnIOThread(ResourceContext* resource_context) {
ResourceContext::GetAppCacheService(resource_context)->PurgeMemory();
}
-DOMStorageContextImpl* GetDOMStorageContextImpl(BrowserContext* context) {
+DOMStorageContextImpl* GetDefaultDOMStorageContextImpl(
+ BrowserContext* context) {
return static_cast<DOMStorageContextImpl*>(
- BrowserContext::GetDOMStorageContext(context));
+ BrowserContext::GetDefaultDOMStorageContext(context));
}
} // namespace
@@ -165,42 +332,70 @@ DownloadManager* BrowserContext::GetDownloadManager(
context, kDownloadManagerKeyName);
}
-QuotaManager* BrowserContext::GetQuotaManager(BrowserContext* context) {
- CreateQuotaManagerAndClients(context);
- return UserDataAdapter<QuotaManager>::Get(context, kQuotaManagerKeyName);
+QuotaManager* BrowserContext::GetQuotaManager(BrowserContext* browser_context) {
+ // TODO(ajwong): Change this API to require a process id instead of using
+ // kInvalidChildProcessId.
+ StoragePartition* partition =
+ GetStoragePartition(browser_context,
+ ChildProcessHostImpl::kInvalidChildProcessId);
+ return partition->quota_manager();
+}
+
+DOMStorageContext* BrowserContext::GetDefaultDOMStorageContext(
+ BrowserContext* browser_context) {
+ // TODO(ajwong): Force all users to know which process id they performing
Charlie Reis 2012/07/10 21:37:48 nit: they are nit: behavior -> behalf
+ // actions on behavior of, migrate them to GetDOMStorageContext(), and then
+ // delete this function.
+ return GetDOMStorageContext(browser_context,
+ ChildProcessHostImpl::kInvalidChildProcessId);
}
DOMStorageContext* BrowserContext::GetDOMStorageContext(
- BrowserContext* context) {
- CreateQuotaManagerAndClients(context);
- return UserDataAdapter<DOMStorageContextImpl>::Get(
- context, kDOMStorageContextKeyName);
+ BrowserContext* browser_context,
+ int render_child_id) {
+ StoragePartition* partition =
+ GetStoragePartition(browser_context, render_child_id);
+ return partition->dom_storage_context();
}
-IndexedDBContext* BrowserContext::GetIndexedDBContext(BrowserContext* context) {
- CreateQuotaManagerAndClients(context);
- return UserDataAdapter<IndexedDBContext>::Get(
- context, kIndexedDBContextKeyName);
+IndexedDBContext* BrowserContext::GetIndexedDBContext(
+ BrowserContext* browser_context) {
+ // TODO(ajwong): Change this API to require a process id instead of using
+ // kInvalidChildProcessId.
+ StoragePartition* partition =
+ GetStoragePartition(browser_context,
+ ChildProcessHostImpl::kInvalidChildProcessId);
+ return partition->indexed_db_context();
}
-DatabaseTracker* BrowserContext::GetDatabaseTracker(BrowserContext* context) {
- CreateQuotaManagerAndClients(context);
- return UserDataAdapter<DatabaseTracker>::Get(
- context, kDatabaseTrackerKeyName);
+DatabaseTracker* BrowserContext::GetDatabaseTracker(
+ BrowserContext* browser_context) {
+ // TODO(ajwong): Change this API to require a process id instead of using
+ // kInvalidChildProcessId.
+ StoragePartition* partition =
+ GetStoragePartition(browser_context,
+ ChildProcessHostImpl::kInvalidChildProcessId);
+ return partition->database_tracker();
}
AppCacheService* BrowserContext::GetAppCacheService(
BrowserContext* browser_context) {
- CreateQuotaManagerAndClients(browser_context);
- return UserDataAdapter<ChromeAppCacheService>::Get(
- browser_context, kAppCacheServicKeyName);
+ // TODO(ajwong): Change this API to require a process id instead of using
+ // kInvalidChildProcessId.
+ StoragePartition* partition =
+ GetStoragePartition(browser_context,
+ ChildProcessHostImpl::kInvalidChildProcessId);
+ return partition->appcache_service();
}
FileSystemContext* BrowserContext::GetFileSystemContext(
BrowserContext* browser_context) {
- CreateQuotaManagerAndClients(browser_context);
- return UserDataAdapter<FileSystemContext>::Get(
- browser_context, kFileSystemContextKeyName);
+ // TODO(ajwong): Change this API to require a process id instead of using
+ // kInvalidChildProcessId.
+ StoragePartition* partition =
+ GetStoragePartition(browser_context,
+ ChildProcessHostImpl::kInvalidChildProcessId);
+ return partition->filesystem_context();
}
void BrowserContext::EnsureResourceContextInitialized(BrowserContext* context) {
@@ -208,9 +403,9 @@ void BrowserContext::EnsureResourceContextInitialized(BrowserContext* context) {
// necessary, which initializes ResourceContext. The reason we don't call
// ResourceContext::InitializeResourceContext directly here is that if
// ResourceContext ends up initializing it will call back into BrowserContext
- // and when that call return it'll end rewriting its UserData map (with the
+ // and when that call returns it'll end rewriting its UserData map (with the
// same value) but this causes a race condition. See http://crbug.com/115678.
- CreateQuotaManagerAndClients(context);
+ GetStoragePartition(context, ChildProcessHostImpl::kInvalidChildProcessId);
}
void BrowserContext::SaveSessionState(BrowserContext* browser_context) {
@@ -223,7 +418,10 @@ void BrowserContext::SaveSessionState(BrowserContext* browser_context) {
browser_context->GetResourceContext()));
}
- GetDOMStorageContextImpl(browser_context)->SetForceKeepSessionState();
+ // TODO(ajwong): This is the only usage of GetDefaultDOMStorageContextImpl().
+ // After we migrate this to support multiple DOMStorageContexts, don't forget
+ // to remove the GetDefaultDOMStorageContextImpl() function as well.
+ GetDefaultDOMStorageContextImpl(browser_context)->SetForceKeepSessionState();
if (BrowserThread::IsMessageLoopValid(BrowserThread::WEBKIT_DEPRECATED)) {
IndexedDBContextImpl* indexed_db = static_cast<IndexedDBContextImpl*>(
@@ -243,22 +441,11 @@ void BrowserContext::PurgeMemory(BrowserContext* browser_context) {
browser_context->GetResourceContext()));
}
- GetDOMStorageContextImpl(browser_context)->PurgeMemory();
+ ForEachDOMStorageContext(browser_context,
+ base::Bind(&DOMStorageContextImpl::PurgeMemory));
}
BrowserContext::~BrowserContext() {
- // These message loop checks are just to avoid leaks in unittests.
- if (GetUserData(kDatabaseTrackerKeyName) &&
- BrowserThread::IsMessageLoopValid(BrowserThread::FILE)) {
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- base::Bind(&webkit_database::DatabaseTracker::Shutdown,
- GetDatabaseTracker(this)));
- }
-
- if (GetUserData(kDOMStorageContextKeyName))
- GetDOMStorageContextImpl(this)->Shutdown();
-
if (GetUserData(kDownloadManagerKeyName))
GetDownloadManager(this)->Shutdown();
}

Powered by Google App Engine
This is Rietveld 408576698