summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-11 09:33:42 +0000
committerajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-11 09:33:42 +0000
commit399583b852a0b9de3078f00df019bc9be96b858c (patch)
tree2c5ecf64830945b2e2a38c4cd43e0385488c6fcc /content
parent8717475bbc326c718a0960f6eb69b9f37c50f412 (diff)
downloadchromium_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.cc15
-rw-r--r--content/browser/renderer_host/render_process_host_impl.cc4
-rw-r--r--content/browser/site_instance_impl.cc62
-rw-r--r--content/browser/site_instance_impl.h4
-rw-r--r--content/browser/storage_partition_impl_map.cc127
-rw-r--r--content/browser/storage_partition_impl_map.h21
-rw-r--r--content/public/browser/browser_context.h11
-rw-r--r--content/public/browser/site_instance.h4
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>;