diff options
author | ajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-11 09:33:42 +0000 |
---|---|---|
committer | ajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-11 09:33:42 +0000 |
commit | 399583b852a0b9de3078f00df019bc9be96b858c (patch) | |
tree | 2c5ecf64830945b2e2a38c4cd43e0385488c6fcc /content | |
parent | 8717475bbc326c718a0960f6eb69b9f37c50f412 (diff) | |
download | chromium_src-399583b852a0b9de3078f00df019bc9be96b858c.zip chromium_src-399583b852a0b9de3078f00df019bc9be96b858c.tar.gz chromium_src-399583b852a0b9de3078f00df019bc9be96b858c.tar.bz2 |
Garbage Collect the Storage directory on next profile start after an extension uninstall.
BUG=85127
Review URL: https://chromiumcodereview.appspot.com/11419307
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@172278 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r-- | content/browser/browser_context.cc | 15 | ||||
-rw-r--r-- | content/browser/renderer_host/render_process_host_impl.cc | 4 | ||||
-rw-r--r-- | content/browser/site_instance_impl.cc | 62 | ||||
-rw-r--r-- | content/browser/site_instance_impl.h | 4 | ||||
-rw-r--r-- | content/browser/storage_partition_impl_map.cc | 127 | ||||
-rw-r--r-- | content/browser/storage_partition_impl_map.h | 21 | ||||
-rw-r--r-- | content/public/browser/browser_context.h | 11 | ||||
-rw-r--r-- | content/public/browser/site_instance.h | 4 |
8 files changed, 192 insertions, 56 deletions
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc index 32bfe0d..2385ca0 100644 --- a/content/browser/browser_context.cc +++ b/content/browser/browser_context.cc @@ -94,8 +94,19 @@ void PurgeMemoryOnIOThread(appcache::AppCacheService* appcache_service) { // static void BrowserContext::AsyncObliterateStoragePartition( BrowserContext* browser_context, - const GURL& site) { - GetStoragePartitionMap(browser_context)->AsyncObliterate(site); + const GURL& site, + const base::Closure& on_gc_required) { + GetStoragePartitionMap(browser_context)->AsyncObliterate(site, + on_gc_required); +} + +// static +void BrowserContext::GarbageCollectStoragePartitions( + BrowserContext* browser_context, + scoped_ptr<base::hash_set<FilePath> > active_paths, + const base::Closure& done) { + GetStoragePartitionMap(browser_context)->GarbageCollect( + active_paths.Pass(), done); } DownloadManager* BrowserContext::GetDownloadManager( diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index d50a905..677bc0d 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -1401,7 +1401,7 @@ RenderProcessHost* RenderProcessHostImpl::GetProcessHostForSite( // See if we have an existing process for this site. If not, the caller // should create a new process and register it. - std::string site = SiteInstanceImpl::GetSiteForURL(browser_context, url) + std::string site = SiteInstance::GetSiteForURL(browser_context, url) .possibly_invalid_spec(); return map->FindProcess(site); } @@ -1417,7 +1417,7 @@ void RenderProcessHostImpl::RegisterProcessHostForSite( // TODO(creis): Determine if it's better to allow registration of // empty sites or not. For now, group anything from which we can't parse // a site into the same process, when using --process-per-site. - std::string site = SiteInstanceImpl::GetSiteForURL(browser_context, url) + std::string site = SiteInstance::GetSiteForURL(browser_context, url) .possibly_invalid_spec(); map->RegisterProcess(site, process); } diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc index 5899791..14e1651 100644 --- a/content/browser/site_instance_impl.cc +++ b/content/browser/site_instance_impl.cc @@ -237,8 +237,37 @@ SiteInstance* SiteInstance::CreateForURL(BrowserContext* browser_context, } /*static*/ -GURL SiteInstanceImpl::GetSiteForURL(BrowserContext* browser_context, - const GURL& real_url) { +bool SiteInstance::IsSameWebSite(BrowserContext* browser_context, + const GURL& real_url1, + const GURL& real_url2) { + GURL url1 = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url1); + GURL url2 = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url2); + + // We infer web site boundaries based on the registered domain name of the + // top-level page and the scheme. We do not pay attention to the port if + // one is present, because pages served from different ports can still + // access each other if they change their document.domain variable. + + // Some special URLs will match the site instance of any other URL. This is + // done before checking both of them for validity, since we want these URLs + // to have the same site instance as even an invalid one. + if (IsURLSameAsAnySiteInstance(url1) || IsURLSameAsAnySiteInstance(url2)) + return true; + + // If either URL is invalid, they aren't part of the same site. + if (!url1.is_valid() || !url2.is_valid()) + return false; + + // If the schemes differ, they aren't part of the same site. + if (url1.scheme() != url2.scheme()) + return false; + + return net::RegistryControlledDomainService::SameDomainOrHost(url1, url2); +} + +/*static*/ +GURL SiteInstance::GetSiteForURL(BrowserContext* browser_context, + const GURL& real_url) { // TODO(fsamuel, creis): For some reason appID is not recognized as a host. if (real_url.SchemeIs(chrome::kGuestScheme)) return real_url; @@ -277,35 +306,6 @@ GURL SiteInstanceImpl::GetSiteForURL(BrowserContext* browser_context, } /*static*/ -bool SiteInstance::IsSameWebSite(BrowserContext* browser_context, - const GURL& real_url1, - const GURL& real_url2) { - GURL url1 = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url1); - GURL url2 = SiteInstanceImpl::GetEffectiveURL(browser_context, real_url2); - - // We infer web site boundaries based on the registered domain name of the - // top-level page and the scheme. We do not pay attention to the port if - // one is present, because pages served from different ports can still - // access each other if they change their document.domain variable. - - // Some special URLs will match the site instance of any other URL. This is - // done before checking both of them for validity, since we want these URLs - // to have the same site instance as even an invalid one. - if (IsURLSameAsAnySiteInstance(url1) || IsURLSameAsAnySiteInstance(url2)) - return true; - - // If either URL is invalid, they aren't part of the same site. - if (!url1.is_valid() || !url2.is_valid()) - return false; - - // If the schemes differ, they aren't part of the same site. - if (url1.scheme() != url2.scheme()) - return false; - - return net::RegistryControlledDomainService::SameDomainOrHost(url1, url2); -} - -/*static*/ GURL SiteInstanceImpl::GetEffectiveURL(BrowserContext* browser_context, const GURL& url) { return GetContentClient()->browser()-> diff --git a/content/browser/site_instance_impl.h b/content/browser/site_instance_impl.h index 1f15e59..fda0d37 100644 --- a/content/browser/site_instance_impl.h +++ b/content/browser/site_instance_impl.h @@ -53,10 +53,6 @@ class CONTENT_EXPORT SiteInstanceImpl : public SiteInstance, render_process_host_factory_ = rph_factory; } - // Returns the site for the given URL, which includes only the scheme and - // registered domain. Returns an empty GURL if the URL has no host. - static GURL GetSiteForURL(BrowserContext* context, const GURL& url); - protected: friend class BrowsingInstance; friend class SiteInstance; diff --git a/content/browser/storage_partition_impl_map.cc b/content/browser/storage_partition_impl_map.cc index 06f3dc4..12768eb 100644 --- a/content/browser/storage_partition_impl_map.cc +++ b/content/browser/storage_partition_impl_map.cc @@ -9,9 +9,10 @@ #include "base/file_path.h" #include "base/file_util.h" #include "base/stl_util.h" -#include "base/string_util.h" #include "base/string_number_conversions.h" -#include "base/threading/worker_pool.h" +#include "base/string_util.h" +#include "base/stringprintf.h" +#include "base/threading/sequenced_worker_pool.h" #include "content/browser/appcache/chrome_appcache_service.h" #include "content/browser/fileapi/browser_file_system_helper.h" #include "content/browser/fileapi/chrome_blob_storage_context.h" @@ -210,6 +211,8 @@ const FilePath::CharType kExtensionsDirname[] = FILE_PATH_LITERAL("ext"); const FilePath::CharType kDefaultPartitionDirname[] = FILE_PATH_LITERAL("def"); +const FilePath::CharType kTrashDirname[] = + FILE_PATH_LITERAL("trash"); // Because partition names are user specified, they can be arbitrarily long // which makes them unsuitable for paths names. We use a truncation of a @@ -267,6 +270,8 @@ FilePath GetStoragePartitionDomainPath( void ObliterateOneDirectory(const FilePath& current_dir, const std::vector<FilePath>& paths_to_keep, std::vector<FilePath>* paths_to_consider) { + CHECK(current_dir.IsAbsolute()); + file_util::FileEnumerator enumerator(current_dir, false, kAllFileTypes); for (FilePath to_delete = enumerator.Next(); !to_delete.empty(); to_delete = enumerator.Next()) { @@ -305,8 +310,13 @@ void ObliterateOneDirectory(const FilePath& current_dir, // Synchronously attempts to delete |root|, preserving only entries in // |paths_to_keep|. If there are no entries in |paths_to_keep| on disk, then it // completely removes |root|. All paths must be absolute paths. -void BlockingObliteratePath(const FilePath& root, - const std::vector<FilePath>& paths_to_keep) { +void BlockingObliteratePath( + const FilePath& root, + const std::vector<FilePath>& paths_to_keep, + const scoped_refptr<base::TaskRunner>& closure_runner, + const base::Closure& on_gc_required) { + CHECK(root.IsAbsolute()); + // Reduce |paths_to_keep| set to those under the root and actually on disk. std::vector<FilePath> valid_paths_to_keep; for (std::vector<FilePath>::const_iterator it = paths_to_keep.begin(); @@ -317,11 +327,13 @@ void BlockingObliteratePath(const FilePath& root, } // If none of the |paths_to_keep| are valid anymore then we just whack the - // root and be done with it. + // root and be done with it. Otherwise, signal garbage collection and do + // a best-effort delete of the on-disk structures. if (valid_paths_to_keep.empty()) { file_util::Delete(root, true); return; } + closure_runner->PostTask(FROM_HERE, on_gc_required); // Otherwise, start at the root and delete everything that is not in // |valid_paths_to_keep|. @@ -334,6 +346,52 @@ void BlockingObliteratePath(const FilePath& root, } } +// Deletes all entries inside the |storage_root| that are not in the +// |active_paths|. Deletion is done in 2 steps: +// +// (1) Moving all garbage collected paths into a trash directory. +// (2) Asynchronously deleting the trash directory. +// +// The deletion is asynchronous because after (1) completes, calling code can +// safely continue to use the paths that had just been garbage collected +// without fear of race conditions. +// +// This code also ignores failed moves rather than attempting a smarter retry. +// Moves shouldn't fail here unless there is some out-of-band error (eg., +// FS corruption). Retry logic is dangerous in the general case because +// there is not necessarily a guaranteed case where the logic may succeed. +// +// This function is still named BlockingGarbageCollect() because it does +// execute a few filesystem operations synchronously. +void BlockingGarbageCollect( + const FilePath& storage_root, + const scoped_refptr<base::TaskRunner>& file_access_runner, + scoped_ptr<base::hash_set<FilePath> > active_paths) { + CHECK(storage_root.IsAbsolute()); + + file_util::FileEnumerator enumerator(storage_root, false, kAllFileTypes); + FilePath trash_directory; + if (!file_util::CreateTemporaryDirInDir(storage_root, kTrashDirname, + &trash_directory)) { + // Unable to continue without creating the trash directory so give up. + return; + } + for (FilePath path = enumerator.Next(); !path.empty(); + path = enumerator.Next()) { + if (active_paths->find(path) == active_paths->end() && + path != trash_directory) { + // Since |trash_directory| is unique for each run of this function there + // can be no colllisions on the move. + file_util::Move(path, trash_directory.Append(path.BaseName())); + } + } + + file_access_runner->PostTask( + FROM_HERE, + base::Bind(base::IgnoreResult(&file_util::Delete), trash_directory, + true)); +} + } // namespace // static @@ -365,6 +423,10 @@ StoragePartitionImplMap::StoragePartitionImplMap( BrowserContext* browser_context) : browser_context_(browser_context), resource_context_initialized_(false) { + // Doing here instead of initializer list cause it's just too ugly to read. + base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool(); + file_access_runner_ = + blocking_pool->GetSequencedTaskRunner(blocking_pool->GetSequenceToken()); } StoragePartitionImplMap::~StoragePartitionImplMap() { @@ -418,7 +480,9 @@ StoragePartitionImpl* StoragePartitionImplMap::Get( return partition; } -void StoragePartitionImplMap::AsyncObliterate(const GURL& site) { +void StoragePartitionImplMap::AsyncObliterate( + const GURL& site, + const base::Closure& on_gc_required) { // This method should avoid creating any StoragePartition (which would // create more open file handles) so that it can delete as much of the // data off disk as possible. @@ -429,10 +493,6 @@ void StoragePartitionImplMap::AsyncObliterate(const GURL& site) { browser_context_, site, false, &partition_domain, &partition_name, &in_memory); - // The default partition is the whole profile. We can't obliterate that and - // should never even try. - CHECK(!partition_domain.empty()) << site; - // Find the active partitions for the domain. Because these partitions are // active, it is not possible to just delete the directories that contain // the backing data structures without causing the browser to crash. Instead, @@ -459,13 +519,50 @@ void StoragePartitionImplMap::AsyncObliterate(const GURL& site) { // run of the browser. FilePath domain_root = browser_context_->GetPath().Append( GetStoragePartitionDomainPath(partition_domain)); - base::WorkerPool::PostTask( + + // Early exit required because file_util::ContainsPath() will fail on POSIX + // if |domain_root| does not exist. + if (!file_util::PathExists(domain_root)) { + return; + } + + // Never try to obliterate things outside of the browser context root or the + // browser context root itself. Die hard. + FilePath browser_context_root = browser_context_->GetPath(); + CHECK(file_util::AbsolutePath(&domain_root)); + CHECK(file_util::AbsolutePath(&browser_context_root)); + CHECK(file_util::ContainsPath(browser_context_root, domain_root) && + browser_context_root != domain_root) << site; + + BrowserThread::PostBlockingPoolTask( FROM_HERE, - base::Bind(&BlockingObliteratePath, domain_root, paths_to_keep), - true); + base::Bind(&BlockingObliteratePath, domain_root, paths_to_keep, + base::MessageLoopProxy::current(), on_gc_required)); +} - // TODO(ajwong): Schedule a final AsyncObliterate of the whole directory on - // the next browser start. http://crbug.com/85127. +void StoragePartitionImplMap::GarbageCollect( + scoped_ptr<base::hash_set<FilePath> > active_paths, + const base::Closure& done) { + // Include all paths for current StoragePartitions in the active_paths since + // they cannot be deleted safely. + for (PartitionMap::const_iterator it = partitions_.begin(); + it != partitions_.end(); + ++it) { + const StoragePartitionConfig& config = it->first; + if (!config.in_memory) + active_paths->insert(it->second->GetPath()); + } + + // Find the directory holding the StoragePartitions and delete everything in + // there that isn't considered active. + FilePath storage_root = browser_context_->GetPath().Append( + GetStoragePartitionDomainPath(std::string())); + file_access_runner_->PostTaskAndReply( + FROM_HERE, + base::Bind(&BlockingGarbageCollect, storage_root, + file_access_runner_, + base::Passed(&active_paths)), + done); } void StoragePartitionImplMap::ForEach( diff --git a/content/browser/storage_partition_impl_map.h b/content/browser/storage_partition_impl_map.h index 5c25e02..196a388 100644 --- a/content/browser/storage_partition_impl_map.h +++ b/content/browser/storage_partition_impl_map.h @@ -10,12 +10,17 @@ #include "base/callback_forward.h" #include "base/gtest_prod_util.h" +#include "base/hash_tables.h" #include "base/supports_user_data.h" #include "content/browser/storage_partition_impl.h" #include "content/public/browser/browser_context.h" class FilePath; +namespace base { +class SequencedTaskRunner; +} // namespace base + namespace content { class BrowserContext; @@ -34,7 +39,20 @@ class StoragePartitionImplMap : public base::SupportsUserData::Data { // Starts an asynchronous best-effort attempt to delete all on-disk storage // related to |site|, avoiding any directories that are known to be in use. - void AsyncObliterate(const GURL& site); + // + // |on_gc_required| is called if the AsyncObliterate() call was unable to + // fully clean the on-disk storage requiring a call to GarbageCollect() on + // the next browser start. + void AsyncObliterate(const GURL& site, const base::Closure& on_gc_required); + + // Examines the on-disk storage and removes any entires that are not listed + // in the |active_paths|, or in use by current entries in the storage + // partition. + // + // The |done| closure is executed on the calling thread when garbage + // collection is complete. + void GarbageCollect(scoped_ptr<base::hash_set<FilePath> > active_paths, + const base::Closure& done); void ForEach(const BrowserContext::StoragePartitionCallback& callback); @@ -101,6 +119,7 @@ class StoragePartitionImplMap : public base::SupportsUserData::Data { bool in_memory); BrowserContext* browser_context_; // Not Owned. + scoped_refptr<base::SequencedTaskRunner> file_access_runner_; PartitionMap partitions_; // Set to true when the ResourceContext for the associated |browser_context_| diff --git a/content/public/browser/browser_context.h b/content/public/browser/browser_context.h index ff4446f..f2837f7 100644 --- a/content/public/browser/browser_context.h +++ b/content/public/browser/browser_context.h @@ -7,6 +7,7 @@ #include "base/callback_forward.h" #include "base/hash_tables.h" +#include "base/memory/scoped_ptr.h" #include "base/supports_user_data.h" #include "content/common/content_export.h" @@ -52,7 +53,15 @@ class CONTENT_EXPORT BrowserContext : public base::SupportsUserData { const StoragePartitionCallback& callback); static void AsyncObliterateStoragePartition( BrowserContext* browser_context, - const GURL& site); + const GURL& site, + const base::Closure& on_gc_required); + + // This function clears the contents of |active_paths| but does not take + // ownership of the pointer. + static void GarbageCollectStoragePartitions( + BrowserContext* browser_context, + scoped_ptr<base::hash_set<FilePath> > active_paths, + const base::Closure& done); // DON'T USE THIS. GetDefaultStoragePartition() is going away. // Use GetStoragePartition() instead. Ask ajwong@ if you have problems. diff --git a/content/public/browser/site_instance.h b/content/public/browser/site_instance.h index 69df311..0696fdc 100644 --- a/content/public/browser/site_instance.h +++ b/content/public/browser/site_instance.h @@ -134,6 +134,10 @@ class CONTENT_EXPORT SiteInstance : public base::RefCounted<SiteInstance> { static bool IsSameWebSite(content::BrowserContext* browser_context, const GURL& url1, const GURL& url2); + // Returns the site for the given URL, which includes only the scheme and + // registered domain. Returns an empty GURL if the URL has no host. + static GURL GetSiteForURL(BrowserContext* context, const GURL& url); + protected: friend class base::RefCounted<SiteInstance>; |