Index: content/browser/browser_context.cc |
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc |
index 19dcf236104ed73e3bd5601d79d6cd48cc8a113c..59338f44df6d603ac881acd497d79ef7fa8c1d59 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,93 +34,257 @@ 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) { |
- // Ensure that these methods are called on the UI thread, except for unittests |
- // where a UI thread might not have been created. |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
- !BrowserThread::IsMessageLoopValid(BrowserThread::UI)); |
- 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) { |
+ // Ensure that these methods are called on the UI thread, except for |
+ // unittests where a UI thread might not have been created. |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
+ !BrowserThread::IsMessageLoopValid(BrowserThread::UI)); |
+ |
+ // 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_; |
jam
2012/07/11 02:09:25
nit: fit on one line where you can for readability
awong
2012/07/11 21:17:17
Done.
|
+ } |
+ 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) { |
@@ -138,9 +305,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 |
@@ -170,42 +338,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 are performing |
+ // actions on behalf 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) { |
@@ -213,9 +409,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) { |
@@ -228,7 +424,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*>( |
@@ -248,22 +447,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(); |
} |