summaryrefslogtreecommitdiffstats
path: root/chrome/browser/browsing_data
diff options
context:
space:
mode:
authormarkusheintz@chromium.org <markusheintz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-19 19:22:47 +0000
committermarkusheintz@chromium.org <markusheintz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-19 19:22:47 +0000
commitb0cb5e82de3a8195c91aecdac90bcd8fcc77bdd8 (patch)
tree15a6587d385b54fe41e83b724747eb0d2dc4388e /chrome/browser/browsing_data
parent3cbacdcbc6765a739ca5a1edf13c2dd20be13bf1 (diff)
downloadchromium_src-b0cb5e82de3a8195c91aecdac90bcd8fcc77bdd8.zip
chromium_src-b0cb5e82de3a8195c91aecdac90bcd8fcc77bdd8.tar.gz
chromium_src-b0cb5e82de3a8195c91aecdac90bcd8fcc77bdd8.tar.bz2
Move browsing_data_helper files into a separate directory.
BUG=121862 TEST=existing unit_tests TBR=eroman@chromium.org,sky@chromium.org,erg@chromium.org Review URL: https://chromiumcodereview.appspot.com/10805015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@147504 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/browsing_data')
-rw-r--r--chrome/browser/browsing_data/OWNERS4
-rw-r--r--chrome/browser/browsing_data/browsing_data_appcache_helper.cc156
-rw-r--r--chrome/browser/browsing_data/browsing_data_appcache_helper.h93
-rw-r--r--chrome/browser/browsing_data/browsing_data_appcache_helper_unittest.cc122
-rw-r--r--chrome/browser/browsing_data/browsing_data_cookie_helper.cc230
-rw-r--r--chrome/browser/browsing_data/browsing_data_cookie_helper.h147
-rw-r--r--chrome/browser/browsing_data/browsing_data_cookie_helper_unittest.cc233
-rw-r--r--chrome/browser/browsing_data/browsing_data_database_helper.cc240
-rw-r--r--chrome/browser/browsing_data/browsing_data_database_helper.h167
-rw-r--r--chrome/browser/browsing_data/browsing_data_database_helper_browsertest.cc151
-rw-r--r--chrome/browser/browsing_data/browsing_data_database_helper_unittest.cc63
-rw-r--r--chrome/browser/browsing_data/browsing_data_file_system_helper.cc283
-rw-r--r--chrome/browser/browsing_data/browsing_data_file_system_helper.h178
-rw-r--r--chrome/browser/browsing_data/browsing_data_file_system_helper_unittest.cc318
-rw-r--r--chrome/browser/browsing_data/browsing_data_helper.cc81
-rw-r--r--chrome/browser/browsing_data/browsing_data_helper.h53
-rw-r--r--chrome/browser/browsing_data/browsing_data_helper_browsertest.h45
-rw-r--r--chrome/browser/browsing_data/browsing_data_helper_unittest.cc159
-rw-r--r--chrome/browser/browsing_data/browsing_data_indexed_db_helper.cc263
-rw-r--r--chrome/browser/browsing_data/browsing_data_indexed_db_helper.h145
-rw-r--r--chrome/browser/browsing_data/browsing_data_indexed_db_helper_browsertest.cc73
-rw-r--r--chrome/browser/browsing_data/browsing_data_indexed_db_helper_unittest.cc44
-rw-r--r--chrome/browser/browsing_data/browsing_data_local_storage_helper.cc150
-rw-r--r--chrome/browser/browsing_data/browsing_data_local_storage_helper.h117
-rw-r--r--chrome/browser/browsing_data/browsing_data_local_storage_helper_browsertest.cc191
-rw-r--r--chrome/browser/browsing_data/browsing_data_local_storage_helper_unittest.cc45
-rw-r--r--chrome/browser/browsing_data/browsing_data_quota_helper.cc53
-rw-r--r--chrome/browser/browsing_data/browsing_data_quota_helper.h81
-rw-r--r--chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc178
-rw-r--r--chrome/browser/browsing_data/browsing_data_quota_helper_impl.h77
-rw-r--r--chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc209
-rw-r--r--chrome/browser/browsing_data/browsing_data_remover.cc887
-rw-r--r--chrome/browser/browsing_data/browsing_data_remover.h367
-rw-r--r--chrome/browser/browsing_data/browsing_data_remover_unittest.cc1156
-rw-r--r--chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.cc196
-rw-r--r--chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.h91
-rw-r--r--chrome/browser/browsing_data/browsing_data_server_bound_cert_helper_unittest.cc159
-rw-r--r--chrome/browser/browsing_data/local_data_container.cc184
-rw-r--r--chrome/browser/browsing_data/local_data_container.h141
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_appcache_helper.cc24
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_appcache_helper.h23
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_cookie_helper.cc68
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_cookie_helper.h49
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_database_helper.cc60
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_database_helper.h56
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_file_system_helper.cc61
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_file_system_helper.h58
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_indexed_db_helper.cc57
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_indexed_db_helper.h50
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_local_storage_helper.cc60
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_local_storage_helper.h54
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_quota_helper.cc43
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_quota_helper.h34
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_server_bound_cert_helper.cc61
-rw-r--r--chrome/browser/browsing_data/mock_browsing_data_server_bound_cert_helper.h47
55 files changed, 8335 insertions, 0 deletions
diff --git a/chrome/browser/browsing_data/OWNERS b/chrome/browser/browsing_data/OWNERS
new file mode 100644
index 0000000..227c668
--- /dev/null
+++ b/chrome/browser/browsing_data/OWNERS
@@ -0,0 +1,4 @@
+markusheintz@chromium.org
+mkwst@chromium.org
+bauerb@chromim.org
+michaeln@chromium.org
diff --git a/chrome/browser/browsing_data/browsing_data_appcache_helper.cc b/chrome/browser/browsing_data/browsing_data_appcache_helper.cc
new file mode 100644
index 0000000..50a62e3
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_appcache_helper.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_appcache_helper.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/resource_context.h"
+#include "webkit/appcache/appcache_database.h"
+#include "webkit/appcache/appcache_storage.h"
+
+using appcache::AppCacheDatabase;
+using content::BrowserContext;
+using content::BrowserThread;
+using content::ResourceContext;
+
+BrowsingDataAppCacheHelper::BrowsingDataAppCacheHelper(Profile* profile)
+ : is_fetching_(false),
+ resource_context_(profile->GetResourceContext()) {
+}
+
+void BrowsingDataAppCacheHelper::StartFetching(const base::Closure& callback) {
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ DCHECK(!is_fetching_);
+ DCHECK_EQ(false, callback.is_null());
+ is_fetching_ = true;
+ info_collection_ = new appcache::AppCacheInfoCollection;
+ completion_callback_ = callback;
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrowsingDataAppCacheHelper::StartFetching, this, callback));
+ return;
+ }
+
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ appcache_info_callback_.Reset(
+ base::Bind(&BrowsingDataAppCacheHelper::OnFetchComplete,
+ base::Unretained(this)));
+ ResourceContext::GetAppCacheService(resource_context_)->
+ GetAllAppCacheInfo(info_collection_, appcache_info_callback_.callback());
+}
+
+void BrowsingDataAppCacheHelper::DeleteAppCacheGroup(
+ const GURL& manifest_url) {
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrowsingDataAppCacheHelper::DeleteAppCacheGroup, this,
+ manifest_url));
+ return;
+ }
+
+ ResourceContext::GetAppCacheService(resource_context_)->DeleteAppCacheGroup(
+ manifest_url, net::CompletionCallback());
+}
+
+BrowsingDataAppCacheHelper::~BrowsingDataAppCacheHelper() {}
+
+void BrowsingDataAppCacheHelper::OnFetchComplete(int rv) {
+ if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ // Filter out appcache info entries for non-websafe schemes. Extension state
+ // and DevTools, for example, are not considered browsing data.
+ typedef std::map<GURL, appcache::AppCacheInfoVector> InfoByOrigin;
+ InfoByOrigin& origin_map = info_collection_->infos_by_origin;
+ for (InfoByOrigin::iterator origin = origin_map.begin();
+ origin != origin_map.end();) {
+ InfoByOrigin::iterator current = origin;
+ ++origin;
+ if (!BrowsingDataHelper::HasWebScheme(current->first))
+ origin_map.erase(current);
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&BrowsingDataAppCacheHelper::OnFetchComplete, this, rv));
+ return;
+ }
+
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(is_fetching_);
+ is_fetching_ = false;
+ completion_callback_.Run();
+ completion_callback_.Reset();
+}
+
+CannedBrowsingDataAppCacheHelper::CannedBrowsingDataAppCacheHelper(
+ Profile* profile)
+ : BrowsingDataAppCacheHelper(profile),
+ profile_(profile) {
+ info_collection_ = new appcache::AppCacheInfoCollection;
+}
+
+CannedBrowsingDataAppCacheHelper* CannedBrowsingDataAppCacheHelper::Clone() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ CannedBrowsingDataAppCacheHelper* clone =
+ new CannedBrowsingDataAppCacheHelper(profile_);
+
+ clone->info_collection_->infos_by_origin = info_collection_->infos_by_origin;
+ return clone;
+}
+
+void CannedBrowsingDataAppCacheHelper::AddAppCache(const GURL& manifest_url) {
+ if (!BrowsingDataHelper::HasWebScheme(manifest_url))
+ return; // Ignore non-websafe schemes.
+
+ OriginAppCacheInfoMap& origin_map = info_collection_->infos_by_origin;
+ appcache::AppCacheInfoVector& appcache_infos_ =
+ origin_map[manifest_url.GetOrigin()];
+
+ for (appcache::AppCacheInfoVector::iterator
+ appcache = appcache_infos_.begin(); appcache != appcache_infos_.end();
+ ++appcache) {
+ if (appcache->manifest_url == manifest_url)
+ return;
+ }
+
+ appcache::AppCacheInfo info;
+ info.manifest_url = manifest_url;
+ appcache_infos_.push_back(info);
+}
+
+void CannedBrowsingDataAppCacheHelper::Reset() {
+ info_collection_->infos_by_origin.clear();
+}
+
+bool CannedBrowsingDataAppCacheHelper::empty() const {
+ return info_collection_->infos_by_origin.empty();
+}
+
+size_t CannedBrowsingDataAppCacheHelper::GetAppCacheCount() const {
+ size_t count = 0;
+ const OriginAppCacheInfoMap& map = info_collection_->infos_by_origin;
+ for (OriginAppCacheInfoMap::const_iterator it = map.begin();
+ it != map.end();
+ ++it) {
+ count += it->second.size();
+ }
+ return count;
+}
+
+const BrowsingDataAppCacheHelper::OriginAppCacheInfoMap&
+CannedBrowsingDataAppCacheHelper::GetOriginAppCacheInfoMap() const {
+ return info_collection_->infos_by_origin;
+}
+
+void CannedBrowsingDataAppCacheHelper::StartFetching(
+ const base::Closure& completion_callback) {
+ completion_callback.Run();
+}
+
+CannedBrowsingDataAppCacheHelper::~CannedBrowsingDataAppCacheHelper() {}
diff --git a/chrome/browser/browsing_data/browsing_data_appcache_helper.h b/chrome/browser/browsing_data/browsing_data_appcache_helper.h
new file mode 100644
index 0000000..3b17fbb
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_appcache_helper.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_APPCACHE_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_APPCACHE_HELPER_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "net/base/completion_callback.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/appcache/appcache_service.h"
+
+class Profile;
+
+namespace content {
+class ResourceContext;
+}
+
+// This class fetches appcache information on behalf of a caller
+// on the UI thread.
+class BrowsingDataAppCacheHelper
+ : public base::RefCountedThreadSafe<BrowsingDataAppCacheHelper> {
+ public:
+ typedef std::map<GURL, appcache::AppCacheInfoVector> OriginAppCacheInfoMap;
+
+ explicit BrowsingDataAppCacheHelper(Profile* profile);
+
+ virtual void StartFetching(const base::Closure& completion_callback);
+ virtual void DeleteAppCacheGroup(const GURL& manifest_url);
+
+ appcache::AppCacheInfoCollection* info_collection() const {
+ DCHECK(!is_fetching_);
+ return info_collection_;
+ }
+
+ protected:
+ friend class base::RefCountedThreadSafe<BrowsingDataAppCacheHelper>;
+ virtual ~BrowsingDataAppCacheHelper();
+
+ base::Closure completion_callback_;
+ scoped_refptr<appcache::AppCacheInfoCollection> info_collection_;
+
+ private:
+ void OnFetchComplete(int rv);
+
+ bool is_fetching_;
+ content::ResourceContext* resource_context_;
+ net::CancelableCompletionCallback appcache_info_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataAppCacheHelper);
+};
+
+// This class is a thin wrapper around BrowsingDataAppCacheHelper that does not
+// fetch its information from the appcache service, but gets them passed as
+// a parameter during construction.
+class CannedBrowsingDataAppCacheHelper : public BrowsingDataAppCacheHelper {
+ public:
+ explicit CannedBrowsingDataAppCacheHelper(Profile* profile);
+
+ // Return a copy of the appcache helper. Only one consumer can use the
+ // StartFetching method at a time, so we need to create a copy of the helper
+ // every time we instantiate a cookies tree model for it.
+ CannedBrowsingDataAppCacheHelper* Clone();
+
+ // Add an appcache to the set of canned caches that is returned by this
+ // helper.
+ void AddAppCache(const GURL& manifest_url);
+
+ // Clears the list of canned caches.
+ void Reset();
+
+ // True if no appcaches are currently stored.
+ bool empty() const;
+
+ // Returns the number of app cache resources.
+ size_t GetAppCacheCount() const;
+
+ // Returns a current map with the |AppCacheInfoVector|s per origin.
+ const OriginAppCacheInfoMap& GetOriginAppCacheInfoMap() const;
+
+ // BrowsingDataAppCacheHelper methods.
+ virtual void StartFetching(const base::Closure& completion_callback) OVERRIDE;
+
+ private:
+ virtual ~CannedBrowsingDataAppCacheHelper();
+
+ Profile* profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(CannedBrowsingDataAppCacheHelper);
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_APPCACHE_HELPER_H_
diff --git a/chrome/browser/browsing_data/browsing_data_appcache_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_appcache_helper_unittest.cc
new file mode 100644
index 0000000..b138ac7
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_appcache_helper_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_appcache_helper.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/stl_util.h"
+#include "chrome/test/base/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+class TestCompletionCallback {
+ public:
+ TestCompletionCallback()
+ : have_result_(false) {
+ }
+
+ bool have_result() const { return have_result_; }
+
+ void callback() {
+ have_result_ = true;
+ }
+
+ private:
+ bool have_result_;
+};
+
+} // namespace
+
+typedef testing::Test CannedBrowsingDataAppCacheHelperTest;
+
+TEST_F(CannedBrowsingDataAppCacheHelperTest, SetInfo) {
+ TestingProfile profile;
+
+ GURL manifest1("http://example1.com/manifest.xml");
+ GURL manifest2("http://example2.com/path1/manifest.xml");
+ GURL manifest3("http://example2.com/path2/manifest.xml");
+
+ scoped_refptr<CannedBrowsingDataAppCacheHelper> helper(
+ new CannedBrowsingDataAppCacheHelper(&profile));
+ helper->AddAppCache(manifest1);
+ helper->AddAppCache(manifest2);
+ helper->AddAppCache(manifest3);
+
+ TestCompletionCallback callback;
+ helper->StartFetching(base::Bind(&TestCompletionCallback::callback,
+ base::Unretained(&callback)));
+ ASSERT_TRUE(callback.have_result());
+
+ std::map<GURL, appcache::AppCacheInfoVector>& collection =
+ helper->info_collection()->infos_by_origin;
+
+ ASSERT_EQ(2u, collection.size());
+ EXPECT_TRUE(ContainsKey(collection, manifest1.GetOrigin()));
+ ASSERT_EQ(1u, collection[manifest1.GetOrigin()].size());
+ EXPECT_EQ(manifest1, collection[manifest1.GetOrigin()].at(0).manifest_url);
+
+ EXPECT_TRUE(ContainsKey(collection, manifest2.GetOrigin()));
+ EXPECT_EQ(2u, collection[manifest2.GetOrigin()].size());
+ std::set<GURL> manifest_results;
+ manifest_results.insert(collection[manifest2.GetOrigin()].at(0).manifest_url);
+ manifest_results.insert(collection[manifest2.GetOrigin()].at(1).manifest_url);
+ EXPECT_TRUE(ContainsKey(manifest_results, manifest2));
+ EXPECT_TRUE(ContainsKey(manifest_results, manifest3));
+}
+
+TEST_F(CannedBrowsingDataAppCacheHelperTest, Unique) {
+ TestingProfile profile;
+
+ GURL manifest("http://example.com/manifest.xml");
+
+ scoped_refptr<CannedBrowsingDataAppCacheHelper> helper(
+ new CannedBrowsingDataAppCacheHelper(&profile));
+ helper->AddAppCache(manifest);
+ helper->AddAppCache(manifest);
+
+ TestCompletionCallback callback;
+ helper->StartFetching(base::Bind(&TestCompletionCallback::callback,
+ base::Unretained(&callback)));
+ ASSERT_TRUE(callback.have_result());
+
+ std::map<GURL, appcache::AppCacheInfoVector>& collection =
+ helper->info_collection()->infos_by_origin;
+
+ ASSERT_EQ(1u, collection.size());
+ EXPECT_TRUE(ContainsKey(collection, manifest.GetOrigin()));
+ ASSERT_EQ(1u, collection[manifest.GetOrigin()].size());
+ EXPECT_EQ(manifest, collection[manifest.GetOrigin()].at(0).manifest_url);
+}
+
+TEST_F(CannedBrowsingDataAppCacheHelperTest, Empty) {
+ TestingProfile profile;
+
+ GURL manifest("http://example.com/manifest.xml");
+
+ scoped_refptr<CannedBrowsingDataAppCacheHelper> helper(
+ new CannedBrowsingDataAppCacheHelper(&profile));
+
+ ASSERT_TRUE(helper->empty());
+ helper->AddAppCache(manifest);
+ ASSERT_FALSE(helper->empty());
+ helper->Reset();
+ ASSERT_TRUE(helper->empty());
+}
+
+TEST_F(CannedBrowsingDataAppCacheHelperTest, IgnoreExtensionsAndDevTools) {
+ TestingProfile profile;
+
+ GURL manifest1("chrome-extension://abcdefghijklmnopqrstuvwxyz/manifest.xml");
+ GURL manifest2("chrome-devtools://abcdefghijklmnopqrstuvwxyz/manifest.xml");
+
+ scoped_refptr<CannedBrowsingDataAppCacheHelper> helper(
+ new CannedBrowsingDataAppCacheHelper(&profile));
+
+ ASSERT_TRUE(helper->empty());
+ helper->AddAppCache(manifest1);
+ ASSERT_TRUE(helper->empty());
+ helper->AddAppCache(manifest2);
+ ASSERT_TRUE(helper->empty());
+}
diff --git a/chrome/browser/browsing_data/browsing_data_cookie_helper.cc b/chrome/browser/browsing_data/browsing_data_cookie_helper.cc
new file mode 100644
index 0000000..0e261e1
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_cookie_helper.cc
@@ -0,0 +1,230 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_cookie_helper.h"
+
+#include "utility"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_thread.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/registry_controlled_domain.h"
+#include "net/cookies/canonical_cookie.h"
+#include "net/cookies/parsed_cookie.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+
+using content::BrowserThread;
+
+BrowsingDataCookieHelper::BrowsingDataCookieHelper(
+ net::URLRequestContextGetter* request_context_getter)
+ : is_fetching_(false),
+ request_context_getter_(request_context_getter) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+}
+
+BrowsingDataCookieHelper::~BrowsingDataCookieHelper() {
+}
+
+void BrowsingDataCookieHelper::StartFetching(
+ const base::Callback<void(const net::CookieList& cookies)>& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!is_fetching_);
+ DCHECK(!callback.is_null());
+ DCHECK(completion_callback_.is_null());
+ is_fetching_ = true;
+ completion_callback_ = callback;
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrowsingDataCookieHelper::FetchCookiesOnIOThread, this));
+}
+
+void BrowsingDataCookieHelper::DeleteCookie(
+ const net::CanonicalCookie& cookie) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrowsingDataCookieHelper::DeleteCookieOnIOThread,
+ this, cookie));
+}
+
+void BrowsingDataCookieHelper::FetchCookiesOnIOThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ scoped_refptr<net::CookieMonster> cookie_monster =
+ request_context_getter_->GetURLRequestContext()->
+ cookie_store()->GetCookieMonster();
+ if (cookie_monster) {
+ cookie_monster->GetAllCookiesAsync(
+ base::Bind(&BrowsingDataCookieHelper::OnFetchComplete, this));
+ } else {
+ OnFetchComplete(net::CookieList());
+ }
+}
+
+void BrowsingDataCookieHelper::OnFetchComplete(const net::CookieList& cookies) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&BrowsingDataCookieHelper::NotifyInUIThread, this, cookies));
+}
+
+void BrowsingDataCookieHelper::NotifyInUIThread(
+ const net::CookieList& cookies) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(is_fetching_);
+ is_fetching_ = false;
+ completion_callback_.Run(cookies);
+ completion_callback_.Reset();
+}
+
+void BrowsingDataCookieHelper::DeleteCookieOnIOThread(
+ const net::CanonicalCookie& cookie) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ scoped_refptr<net::CookieMonster> cookie_monster =
+ request_context_getter_->GetURLRequestContext()->
+ cookie_store()->GetCookieMonster();
+ if (cookie_monster) {
+ cookie_monster->DeleteCanonicalCookieAsync(
+ cookie, net::CookieMonster::DeleteCookieCallback());
+ }
+}
+
+CannedBrowsingDataCookieHelper::CannedBrowsingDataCookieHelper(
+ net::URLRequestContextGetter* request_context_getter)
+ : BrowsingDataCookieHelper(request_context_getter) {
+}
+
+CannedBrowsingDataCookieHelper::~CannedBrowsingDataCookieHelper() {
+ Reset();
+}
+
+CannedBrowsingDataCookieHelper* CannedBrowsingDataCookieHelper::Clone() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ CannedBrowsingDataCookieHelper* clone =
+ new CannedBrowsingDataCookieHelper(request_context_getter());
+
+ for (OriginCookieListMap::iterator it = origin_cookie_list_map_.begin();
+ it != origin_cookie_list_map_.end();
+ ++it) {
+ net::CookieList* cookies = clone->GetCookiesFor(it->first);
+ cookies->insert(cookies->begin(), it->second->begin(), it->second->end());
+ }
+ return clone;
+}
+
+void CannedBrowsingDataCookieHelper::AddReadCookies(
+ const GURL& frame_url,
+ const GURL& url,
+ const net::CookieList& cookie_list) {
+ typedef net::CookieList::const_iterator cookie_iterator;
+ for (cookie_iterator add_cookie = cookie_list.begin();
+ add_cookie != cookie_list.end(); ++add_cookie) {
+ AddCookie(frame_url, *add_cookie);
+ }
+}
+
+void CannedBrowsingDataCookieHelper::AddChangedCookie(
+ const GURL& frame_url,
+ const GURL& url,
+ const std::string& cookie_line,
+ const net::CookieOptions& options) {
+ net::ParsedCookie parsed_cookie(cookie_line);
+ if (options.exclude_httponly() && parsed_cookie.IsHttpOnly()) {
+ // Return if a Javascript cookie illegally specified the HTTP only flag.
+ return;
+ }
+
+ // This fails to create a canonical cookie, if the normalized cookie domain
+ // form cookie line and the url don't have the same domain+registry, or url
+ // host isn't cookie domain or one of its subdomains.
+ scoped_ptr<net::CanonicalCookie> cookie(
+ net::CanonicalCookie::Create(url, parsed_cookie));
+ if (cookie.get())
+ AddCookie(frame_url, *cookie);
+}
+
+void CannedBrowsingDataCookieHelper::Reset() {
+ STLDeleteContainerPairSecondPointers(origin_cookie_list_map_.begin(),
+ origin_cookie_list_map_.end());
+ origin_cookie_list_map_.clear();
+}
+
+bool CannedBrowsingDataCookieHelper::empty() const {
+ for (OriginCookieListMap::const_iterator it =
+ origin_cookie_list_map_.begin();
+ it != origin_cookie_list_map_.end();
+ ++it) {
+ if (!it->second->empty())
+ return false;
+ }
+ return true;
+}
+
+
+size_t CannedBrowsingDataCookieHelper::GetCookieCount() const {
+ size_t count = 0;
+ for (OriginCookieListMap::const_iterator it = origin_cookie_list_map_.begin();
+ it != origin_cookie_list_map_.end();
+ ++it) {
+ count += it->second->size();
+ }
+ return count;
+}
+
+
+void CannedBrowsingDataCookieHelper::StartFetching(
+ const net::CookieMonster::GetCookieListCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ net::CookieList cookie_list;
+ for (OriginCookieListMap::iterator it = origin_cookie_list_map_.begin();
+ it != origin_cookie_list_map_.end();
+ ++it) {
+ cookie_list.insert(cookie_list.begin(),
+ it->second->begin(),
+ it->second->end());
+ }
+ callback.Run(cookie_list);
+}
+
+bool CannedBrowsingDataCookieHelper::DeleteMatchingCookie(
+ const net::CanonicalCookie& add_cookie,
+ net::CookieList* cookie_list) {
+ typedef net::CookieList::iterator cookie_iterator;
+ for (cookie_iterator cookie = cookie_list->begin();
+ cookie != cookie_list->end(); ++cookie) {
+ if (cookie->Name() == add_cookie.Name() &&
+ cookie->Domain() == add_cookie.Domain()&&
+ cookie->Path() == add_cookie.Path()) {
+ cookie_list->erase(cookie);
+ return true;
+ }
+ }
+ return false;
+}
+
+net::CookieList* CannedBrowsingDataCookieHelper::GetCookiesFor(
+ const GURL& first_party_origin) {
+ OriginCookieListMap::iterator it =
+ origin_cookie_list_map_.find(first_party_origin);
+ if (it == origin_cookie_list_map_.end()) {
+ net::CookieList* cookies = new net::CookieList();
+ origin_cookie_list_map_.insert(
+ std::pair<GURL, net::CookieList*>(first_party_origin, cookies));
+ return cookies;
+ }
+ return it->second;
+}
+
+void CannedBrowsingDataCookieHelper::AddCookie(
+ const GURL& frame_url,
+ const net::CanonicalCookie& cookie) {
+ net::CookieList* cookie_list =
+ GetCookiesFor(frame_url.GetOrigin());
+ DeleteMatchingCookie(cookie, cookie_list);
+ cookie_list->push_back(cookie);
+}
diff --git a/chrome/browser/browsing_data/browsing_data_cookie_helper.h b/chrome/browser/browsing_data/browsing_data_cookie_helper.h
new file mode 100644
index 0000000..ad724056
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_cookie_helper.h
@@ -0,0 +1,147 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_COOKIE_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_COOKIE_HELPER_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "net/cookies/cookie_monster.h"
+
+class GURL;
+
+namespace net {
+class CanonicalCookie;
+class URLRequestContextGetter;
+}
+
+// This class fetches cookie information on behalf of a caller
+// on the UI thread.
+// A client of this class need to call StartFetching from the UI thread to
+// initiate the flow, and it'll be notified by the callback in its UI
+// thread at some later point.
+class BrowsingDataCookieHelper
+ : public base::RefCountedThreadSafe<BrowsingDataCookieHelper> {
+ public:
+ explicit BrowsingDataCookieHelper(
+ net::URLRequestContextGetter* request_context_getter);
+
+ // Starts the fetching process, which will notify its completion via
+ // callback.
+ // This must be called only in the UI thread.
+ virtual void StartFetching(
+ const base::Callback<void(const net::CookieList& cookies)>& callback);
+
+ // Requests a single cookie to be deleted in the IO thread. This must be
+ // called in the UI thread.
+ virtual void DeleteCookie(const net::CanonicalCookie& cookie);
+
+ protected:
+ friend class base::RefCountedThreadSafe<BrowsingDataCookieHelper>;
+ virtual ~BrowsingDataCookieHelper();
+
+ net::URLRequestContextGetter* request_context_getter() {
+ return request_context_getter_;
+ }
+
+ private:
+ // Fetch the cookies. This must be called in the IO thread.
+ void FetchCookiesOnIOThread();
+
+ // Callback function for get cookie. This must be called in the IO thread.
+ void OnFetchComplete(const net::CookieList& cookies);
+
+ // Notifies the completion callback. This must be called in the UI thread.
+ void NotifyInUIThread(const net::CookieList& cookies);
+
+ // Delete a single cookie. This must be called in IO thread.
+ void DeleteCookieOnIOThread(const net::CanonicalCookie& cookie);
+
+ // Indicates whether or not we're currently fetching information:
+ // it's true when StartFetching() is called in the UI thread, and it's reset
+ // after we notify the callback in the UI thread.
+ // This only mutates on the UI thread.
+ bool is_fetching_;
+
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+
+ // This only mutates on the UI thread.
+ base::Callback<void(const net::CookieList& cookies)> completion_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataCookieHelper);
+};
+
+// This class is a thin wrapper around BrowsingDataCookieHelper that does not
+// fetch its information from the persistent cookie store, but gets them passed
+// as a parameter during construction.
+class CannedBrowsingDataCookieHelper : public BrowsingDataCookieHelper {
+ public:
+ typedef std::map<GURL, net::CookieList*> OriginCookieListMap;
+
+ explicit CannedBrowsingDataCookieHelper(
+ net::URLRequestContextGetter* request_context);
+
+ // Return a copy of the cookie helper. Only one consumer can use the
+ // StartFetching method at a time, so we need to create a copy of the helper
+ // everytime we instantiate a cookies tree model for it.
+ CannedBrowsingDataCookieHelper* Clone();
+
+ // Adds cookies and delete the current cookies with the same Name, Domain,
+ // and Path as the newly created ones.
+ void AddReadCookies(const GURL& frame_url,
+ const GURL& request_url,
+ const net::CookieList& cookie_list);
+
+ // Adds cookies that will be stored by the CookieMonster. Designed to mirror
+ // the logic of SetCookieWithOptions.
+ void AddChangedCookie(const GURL& frame_url,
+ const GURL& request_url,
+ const std::string& cookie_line,
+ const net::CookieOptions& options);
+
+ // Clears the list of canned cookies.
+ void Reset();
+
+ // True if no cookie are currently stored.
+ bool empty() const;
+
+ // BrowsingDataCookieHelper methods.
+ virtual void StartFetching(
+ const net::CookieMonster::GetCookieListCallback& callback) OVERRIDE;
+
+ // Returns the number of stored cookies.
+ size_t GetCookieCount() const;
+
+ // Returns the map that contains the cookie lists for all frame urls.
+ const OriginCookieListMap& origin_cookie_list_map() {
+ return origin_cookie_list_map_;
+ }
+
+ private:
+ // Check if the cookie list contains a cookie with the same name,
+ // domain, and path as the newly created cookie. Delete the old cookie
+ // if does.
+ bool DeleteMatchingCookie(const net::CanonicalCookie& add_cookie,
+ net::CookieList* cookie_list);
+
+ virtual ~CannedBrowsingDataCookieHelper();
+
+ // Returns the |CookieList| for the given |origin|.
+ net::CookieList* GetCookiesFor(const GURL& origin);
+
+ // Adds the |cookie| to the cookie list for the given |frame_url|.
+ void AddCookie(const GURL& frame_url,
+ const net::CanonicalCookie& cookie);
+
+ // Map that contains the cookie lists for all frame origins.
+ OriginCookieListMap origin_cookie_list_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(CannedBrowsingDataCookieHelper);
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_COOKIE_HELPER_H_
diff --git a/chrome/browser/browsing_data/browsing_data_cookie_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_cookie_helper_unittest.cc
new file mode 100644
index 0000000..d5fc256
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_cookie_helper_unittest.cc
@@ -0,0 +1,233 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_cookie_helper.h"
+
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread.h"
+#include "net/cookies/canonical_cookie.h"
+#include "net/cookies/parsed_cookie.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserThread;
+
+namespace {
+
+class BrowsingDataCookieHelperTest : public testing::Test {
+ public:
+ void SetUpOnIOThread(base::WaitableEvent* io_setup_complete) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ // This is a workaround for a bug in the TestingProfile.
+ // The URLRequestContext will be created by GetCookieMonster on the UI
+ // thread, if it does not already exist. But it must be created on the IO
+ // thread or else it will DCHECK upon destruction.
+ // Force it to be created here.
+ testing_profile_->CreateRequestContext();
+ testing_profile_->GetRequestContext()->GetURLRequestContext();
+ io_setup_complete->Signal();
+ }
+
+ virtual void SetUp() {
+ ui_thread_.reset(new content::TestBrowserThread(BrowserThread::UI,
+ &message_loop_));
+ // Note: we're starting a real IO thread because parts of the
+ // BrowsingDataCookieHelper expect to run on that thread.
+ io_thread_.reset(new content::TestBrowserThread(BrowserThread::IO));
+ ASSERT_TRUE(io_thread_->Start());
+ testing_profile_.reset(new TestingProfile());
+ base::WaitableEvent io_setup_complete(true, false);
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrowsingDataCookieHelperTest::SetUpOnIOThread,
+ base::Unretained(this), &io_setup_complete));
+ io_setup_complete.Wait();
+ }
+
+ virtual void TearDown() {
+ // This must be reset before the IO thread stops, because the
+ // URLRequestContextGetter forces its own deletion to occur on that thread.
+ testing_profile_->ResetRequestContext();
+ io_thread_.reset();
+ ui_thread_.reset();
+ }
+
+ void CreateCookiesForTest() {
+ scoped_refptr<net::CookieMonster> cookie_monster =
+ testing_profile_->GetCookieMonster();
+ cookie_monster->SetCookieWithOptionsAsync(
+ GURL("http://www.google.com"), "A=1", net::CookieOptions(),
+ net::CookieMonster::SetCookiesCallback());
+ cookie_monster->SetCookieWithOptionsAsync(
+ GURL("http://www.gmail.google.com"), "B=1", net::CookieOptions(),
+ net::CookieMonster::SetCookiesCallback());
+ }
+
+ void FetchCallback(const net::CookieList& cookies) {
+ ASSERT_EQ(2UL, cookies.size());
+ cookie_list_ = cookies;
+ net::CookieList::const_iterator it = cookies.begin();
+
+ // Correct because fetching cookies will get a sorted cookie list.
+ ASSERT_TRUE(it != cookies.end());
+ EXPECT_EQ("www.google.com", it->Domain());
+ EXPECT_EQ("A", it->Name());
+
+ ASSERT_TRUE(++it != cookies.end());
+ EXPECT_EQ("www.gmail.google.com", it->Domain());
+ EXPECT_EQ("B", it->Name());
+
+ ASSERT_TRUE(++it == cookies.end());
+ MessageLoop::current()->Quit();
+ }
+
+ void DeleteCallback(const net::CookieList& cookies) {
+ ASSERT_EQ(1UL, cookies.size());
+ net::CookieList::const_iterator it = cookies.begin();
+
+ ASSERT_TRUE(it != cookies.end());
+ EXPECT_EQ("www.gmail.google.com", it->Domain());
+ EXPECT_EQ("B", it->Name());
+
+ ASSERT_TRUE(++it == cookies.end());
+ MessageLoop::current()->Quit();
+ }
+
+ void CannedUniqueCallback(const net::CookieList& cookies) {
+ ASSERT_EQ(1UL, cookies.size());
+ cookie_list_ = cookies;
+ net::CookieList::const_iterator it = cookies.begin();
+
+ ASSERT_TRUE(it != cookies.end());
+ EXPECT_EQ("http://www.google.com/", it->Source());
+ EXPECT_EQ("A", it->Name());
+
+ ASSERT_TRUE(++it == cookies.end());
+ }
+
+ void CannedDifferentFramesCallback(const net::CookieList& cookie_list) {
+ ASSERT_EQ(3U, cookie_list.size());
+ }
+
+ protected:
+ MessageLoop message_loop_;
+ scoped_ptr<content::TestBrowserThread> ui_thread_;
+ scoped_ptr<content::TestBrowserThread> io_thread_;
+ scoped_ptr<TestingProfile> testing_profile_;
+
+ net::CookieList cookie_list_;
+};
+
+TEST_F(BrowsingDataCookieHelperTest, FetchData) {
+ CreateCookiesForTest();
+ scoped_refptr<BrowsingDataCookieHelper> cookie_helper(
+ new BrowsingDataCookieHelper(testing_profile_->GetRequestContext()));
+
+ cookie_helper->StartFetching(
+ base::Bind(&BrowsingDataCookieHelperTest::FetchCallback,
+ base::Unretained(this)));
+
+ // Blocks until BrowsingDataCookieHelperTest::FetchCallback is notified.
+ MessageLoop::current()->Run();
+}
+
+TEST_F(BrowsingDataCookieHelperTest, DeleteCookie) {
+ CreateCookiesForTest();
+ scoped_refptr<BrowsingDataCookieHelper> cookie_helper(
+ new BrowsingDataCookieHelper(testing_profile_->GetRequestContext()));
+
+ cookie_helper->StartFetching(
+ base::Bind(&BrowsingDataCookieHelperTest::FetchCallback,
+ base::Unretained(this)));
+
+ // Blocks until BrowsingDataCookieHelperTest::FetchCallback is notified.
+ MessageLoop::current()->Run();
+
+ net::CanonicalCookie cookie = cookie_list_[0];
+ cookie_helper->DeleteCookie(cookie);
+
+ cookie_helper->StartFetching(
+ base::Bind(&BrowsingDataCookieHelperTest::DeleteCallback,
+ base::Unretained(this)));
+ MessageLoop::current()->Run();
+}
+
+TEST_F(BrowsingDataCookieHelperTest, CannedUnique) {
+ const GURL origin("http://www.google.com");
+ net::CookieList cookie;
+
+ scoped_refptr<CannedBrowsingDataCookieHelper> helper(
+ new CannedBrowsingDataCookieHelper(
+ testing_profile_->GetRequestContext()));
+
+ ASSERT_TRUE(helper->empty());
+ helper->AddChangedCookie(origin, origin, "A=1", net::CookieOptions());
+ helper->AddChangedCookie(origin, origin, "A=1", net::CookieOptions());
+ helper->StartFetching(
+ base::Bind(&BrowsingDataCookieHelperTest::CannedUniqueCallback,
+ base::Unretained(this)));
+ cookie = cookie_list_;
+ helper->Reset();
+ ASSERT_TRUE(helper->empty());
+
+ helper->AddReadCookies(origin, origin, cookie);
+ helper->AddReadCookies(origin, origin, cookie);
+ helper->StartFetching(
+ base::Bind(&BrowsingDataCookieHelperTest::CannedUniqueCallback,
+ base::Unretained(this)));
+}
+
+TEST_F(BrowsingDataCookieHelperTest, CannedEmpty) {
+ const GURL url_google("http://www.google.com");
+
+ scoped_refptr<CannedBrowsingDataCookieHelper> helper(
+ new CannedBrowsingDataCookieHelper(
+ testing_profile_->GetRequestContext()));
+
+ ASSERT_TRUE(helper->empty());
+ helper->AddChangedCookie(url_google, url_google, "a=1",
+ net::CookieOptions());
+ ASSERT_FALSE(helper->empty());
+ helper->Reset();
+ ASSERT_TRUE(helper->empty());
+
+ net::CookieList cookies;
+ net::ParsedCookie pc("a=1");
+ scoped_ptr<net::CanonicalCookie> cookie(
+ new net::CanonicalCookie(url_google, pc));
+ cookies.push_back(*cookie);
+
+ helper->AddReadCookies(url_google, url_google, cookies);
+ ASSERT_FALSE(helper->empty());
+ helper->Reset();
+ ASSERT_TRUE(helper->empty());
+}
+
+TEST_F(BrowsingDataCookieHelperTest, CannedDifferentFrames) {
+ GURL frame1_url("http://www.google.com");
+ GURL frame2_url("http://www.google.de");
+ GURL request_url("http://www.google.com");
+
+ scoped_refptr<CannedBrowsingDataCookieHelper> helper(
+ new CannedBrowsingDataCookieHelper(
+ testing_profile_->GetRequestContext()));
+
+ ASSERT_TRUE(helper->empty());
+ helper->AddChangedCookie(frame1_url, request_url, "a=1",
+ net::CookieOptions());
+ helper->AddChangedCookie(frame1_url, request_url, "b=1",
+ net::CookieOptions());
+ helper->AddChangedCookie(frame2_url, request_url, "c=1",
+ net::CookieOptions());
+
+ helper->StartFetching(
+ base::Bind(&BrowsingDataCookieHelperTest::CannedDifferentFramesCallback,
+ base::Unretained(this)));
+}
+
+} // namespace
diff --git a/chrome/browser/browsing_data/browsing_data_database_helper.cc b/chrome/browser/browsing_data/browsing_data_database_helper.cc
new file mode 100644
index 0000000..32b7c51
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_database_helper.cc
@@ -0,0 +1,240 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_database_helper.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_errors.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCString.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+using WebKit::WebSecurityOrigin;
+
+BrowsingDataDatabaseHelper::DatabaseInfo::DatabaseInfo(
+ const std::string& host,
+ const std::string& database_name,
+ const std::string& origin_identifier,
+ const std::string& description,
+ const std::string& origin,
+ int64 size,
+ base::Time last_modified)
+ : host(host),
+ database_name(database_name),
+ origin_identifier(origin_identifier),
+ description(description),
+ origin(origin),
+ size(size),
+ last_modified(last_modified) {
+}
+
+BrowsingDataDatabaseHelper::DatabaseInfo::~DatabaseInfo() {}
+
+BrowsingDataDatabaseHelper::BrowsingDataDatabaseHelper(Profile* profile)
+ : is_fetching_(false),
+ tracker_(BrowserContext::GetDatabaseTracker(profile)) {
+}
+
+BrowsingDataDatabaseHelper::~BrowsingDataDatabaseHelper() {
+}
+
+void BrowsingDataDatabaseHelper::StartFetching(
+ const base::Callback<void(const std::list<DatabaseInfo>&)>& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!is_fetching_);
+ DCHECK_EQ(false, callback.is_null());
+
+ is_fetching_ = true;
+ database_info_.clear();
+ completion_callback_ = callback;
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&BrowsingDataDatabaseHelper::FetchDatabaseInfoOnFileThread,
+ this));
+}
+
+void BrowsingDataDatabaseHelper::DeleteDatabase(const std::string& origin,
+ const std::string& name) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&BrowsingDataDatabaseHelper::DeleteDatabaseOnFileThread, this,
+ origin, name));
+}
+
+void BrowsingDataDatabaseHelper::FetchDatabaseInfoOnFileThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ std::vector<webkit_database::OriginInfo> origins_info;
+ if (tracker_.get() && tracker_->GetAllOriginsInfo(&origins_info)) {
+ for (std::vector<webkit_database::OriginInfo>::const_iterator ori =
+ origins_info.begin(); ori != origins_info.end(); ++ori) {
+ WebSecurityOrigin web_security_origin =
+ WebSecurityOrigin::createFromDatabaseIdentifier(ori->GetOrigin());
+ GURL origin_url(web_security_origin.toString().utf8());
+ if (!BrowsingDataHelper::HasWebScheme(origin_url)) {
+ // Non-websafe state is not considered browsing data.
+ continue;
+ }
+ std::vector<string16> databases;
+ ori->GetAllDatabaseNames(&databases);
+ for (std::vector<string16>::const_iterator db = databases.begin();
+ db != databases.end(); ++db) {
+ FilePath file_path = tracker_->GetFullDBFilePath(ori->GetOrigin(), *db);
+ base::PlatformFileInfo file_info;
+ if (file_util::GetFileInfo(file_path, &file_info)) {
+ database_info_.push_back(DatabaseInfo(
+ web_security_origin.host().utf8(),
+ UTF16ToUTF8(*db),
+ UTF16ToUTF8(ori->GetOrigin()),
+ UTF16ToUTF8(ori->GetDatabaseDescription(*db)),
+ origin_url.spec(),
+ file_info.size,
+ file_info.last_modified));
+ }
+ }
+ }
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&BrowsingDataDatabaseHelper::NotifyInUIThread, this));
+}
+
+void BrowsingDataDatabaseHelper::NotifyInUIThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(is_fetching_);
+ completion_callback_.Run(database_info_);
+ completion_callback_.Reset();
+ is_fetching_ = false;
+ database_info_.clear();
+}
+
+void BrowsingDataDatabaseHelper::DeleteDatabaseOnFileThread(
+ const std::string& origin,
+ const std::string& name) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ if (!tracker_.get())
+ return;
+ tracker_->DeleteDatabase(UTF8ToUTF16(origin), UTF8ToUTF16(name),
+ net::CompletionCallback());
+}
+
+CannedBrowsingDataDatabaseHelper::PendingDatabaseInfo::PendingDatabaseInfo(
+ const GURL& origin,
+ const std::string& name,
+ const std::string& description)
+ : origin(origin),
+ name(name),
+ description(description) {
+}
+
+CannedBrowsingDataDatabaseHelper::PendingDatabaseInfo::~PendingDatabaseInfo() {}
+
+bool CannedBrowsingDataDatabaseHelper::PendingDatabaseInfo::operator<(
+ const PendingDatabaseInfo& other) const {
+ if (origin == other.origin)
+ return name < other.name;
+ return origin < other.origin;
+}
+
+CannedBrowsingDataDatabaseHelper::CannedBrowsingDataDatabaseHelper(
+ Profile* profile)
+ : BrowsingDataDatabaseHelper(profile),
+ profile_(profile) {
+}
+
+CannedBrowsingDataDatabaseHelper* CannedBrowsingDataDatabaseHelper::Clone() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ CannedBrowsingDataDatabaseHelper* clone =
+ new CannedBrowsingDataDatabaseHelper(profile_);
+
+ base::AutoLock auto_lock(lock_);
+ clone->pending_database_info_ = pending_database_info_;
+ return clone;
+}
+
+void CannedBrowsingDataDatabaseHelper::AddDatabase(
+ const GURL& origin,
+ const std::string& name,
+ const std::string& description) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ base::AutoLock auto_lock(lock_);
+ if (BrowsingDataHelper::HasWebScheme(origin)) {
+ pending_database_info_.insert(PendingDatabaseInfo(
+ origin, name, description));
+ }
+}
+
+void CannedBrowsingDataDatabaseHelper::Reset() {
+ base::AutoLock auto_lock(lock_);
+ pending_database_info_.clear();
+}
+
+bool CannedBrowsingDataDatabaseHelper::empty() const {
+ base::AutoLock auto_lock(lock_);
+ return pending_database_info_.empty();
+}
+
+size_t CannedBrowsingDataDatabaseHelper::GetDatabaseCount() const {
+ base::AutoLock auto_lock(lock_);
+ return pending_database_info_.size();
+}
+
+const std::set<CannedBrowsingDataDatabaseHelper::PendingDatabaseInfo>&
+CannedBrowsingDataDatabaseHelper::GetPendingDatabaseInfo() {
+ return pending_database_info_;
+}
+
+void CannedBrowsingDataDatabaseHelper::StartFetching(
+ const base::Callback<void(const std::list<DatabaseInfo>&)>& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!is_fetching_);
+ DCHECK_EQ(false, callback.is_null());
+
+ is_fetching_ = true;
+ completion_callback_ = callback;
+ BrowserThread::PostTask(
+ BrowserThread::WEBKIT_DEPRECATED, FROM_HERE,
+ base::Bind(&CannedBrowsingDataDatabaseHelper::ConvertInfoInWebKitThread,
+ this));
+}
+
+CannedBrowsingDataDatabaseHelper::~CannedBrowsingDataDatabaseHelper() {}
+
+void CannedBrowsingDataDatabaseHelper::ConvertInfoInWebKitThread() {
+ base::AutoLock auto_lock(lock_);
+ database_info_.clear();
+ for (std::set<PendingDatabaseInfo>::const_iterator
+ info = pending_database_info_.begin();
+ info != pending_database_info_.end(); ++info) {
+ WebSecurityOrigin web_security_origin =
+ WebSecurityOrigin::createFromString(
+ UTF8ToUTF16(info->origin.spec()));
+ std::string origin_identifier =
+ web_security_origin.databaseIdentifier().utf8();
+
+ database_info_.push_back(DatabaseInfo(
+ web_security_origin.host().utf8(),
+ info->name,
+ origin_identifier,
+ info->description,
+ web_security_origin.toString().utf8(),
+ 0,
+ base::Time()));
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&CannedBrowsingDataDatabaseHelper::NotifyInUIThread, this));
+}
diff --git a/chrome/browser/browsing_data/browsing_data_database_helper.h b/chrome/browser/browsing_data/browsing_data_database_helper.h
new file mode 100644
index 0000000..a822bd9
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_database_helper.h
@@ -0,0 +1,167 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_DATABASE_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_DATABASE_HELPER_H_
+
+#include <list>
+#include <set>
+#include <string>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "chrome/common/url_constants.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/database/database_tracker.h"
+
+class Profile;
+
+// This class fetches database information in the FILE thread, and notifies
+// the UI thread upon completion.
+// A client of this class need to call StartFetching from the UI thread to
+// initiate the flow, and it'll be notified by the callback in its UI
+// thread at some later point.
+class BrowsingDataDatabaseHelper
+ : public base::RefCountedThreadSafe<BrowsingDataDatabaseHelper> {
+ public:
+ // Contains detailed information about a web database.
+ struct DatabaseInfo {
+ DatabaseInfo(const std::string& host,
+ const std::string& database_name,
+ const std::string& origin_identifier,
+ const std::string& description,
+ const std::string& origin,
+ int64 size,
+ base::Time last_modified);
+ ~DatabaseInfo();
+
+ std::string host;
+ std::string database_name;
+ std::string origin_identifier;
+ std::string description;
+ std::string origin;
+ int64 size;
+ base::Time last_modified;
+ };
+
+ explicit BrowsingDataDatabaseHelper(Profile* profile);
+
+ // Starts the fetching process, which will notify its completion via
+ // callback.
+ // This must be called only in the UI thread.
+ virtual void StartFetching(
+ const base::Callback<void(const std::list<DatabaseInfo>&)>& callback);
+
+ // Requests a single database to be deleted in the FILE thread. This must be
+ // called in the UI thread.
+ virtual void DeleteDatabase(const std::string& origin,
+ const std::string& name);
+
+ protected:
+ friend class base::RefCountedThreadSafe<BrowsingDataDatabaseHelper>;
+ virtual ~BrowsingDataDatabaseHelper();
+
+ // Notifies the completion callback. This must be called in the UI thread.
+ void NotifyInUIThread();
+
+ // Access to |database_info_| is triggered indirectly via the UI thread and
+ // guarded by |is_fetching_|. This means |database_info_| is only accessed
+ // while |is_fetching_| is true. The flag |is_fetching_| is only accessed on
+ // the UI thread.
+ // In the context of this class |database_info_| is only accessed on the FILE
+ // thread.
+ std::list<DatabaseInfo> database_info_;
+
+ // This only mutates on the UI thread.
+ base::Callback<void(const std::list<DatabaseInfo>&)> completion_callback_;
+
+ // Indicates whether or not we're currently fetching information:
+ // it's true when StartFetching() is called in the UI thread, and it's reset
+ // after we notify the callback in the UI thread.
+ // This only mutates on the UI thread.
+ bool is_fetching_;
+
+ private:
+ // Enumerates all databases. This must be called in the FILE thread.
+ void FetchDatabaseInfoOnFileThread();
+
+ // Delete a single database file. This must be called in the FILE thread.
+ void DeleteDatabaseOnFileThread(const std::string& origin,
+ const std::string& name);
+
+ scoped_refptr<webkit_database::DatabaseTracker> tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataDatabaseHelper);
+};
+
+// This class is a thin wrapper around BrowsingDataDatabaseHelper that does not
+// fetch its information from the database tracker, but gets them passed as
+// a parameter during construction.
+class CannedBrowsingDataDatabaseHelper : public BrowsingDataDatabaseHelper {
+ public:
+ struct PendingDatabaseInfo {
+ PendingDatabaseInfo(const GURL& origin,
+ const std::string& name,
+ const std::string& description);
+ ~PendingDatabaseInfo();
+
+ // The operator is needed to store |PendingDatabaseInfo| objects in a set.
+ bool operator<(const PendingDatabaseInfo& other) const;
+
+ GURL origin;
+ std::string name;
+ std::string description;
+ };
+
+ explicit CannedBrowsingDataDatabaseHelper(Profile* profile);
+
+ // Return a copy of the database helper. Only one consumer can use the
+ // StartFetching method at a time, so we need to create a copy of the helper
+ // everytime we instantiate a cookies tree model for it.
+ CannedBrowsingDataDatabaseHelper* Clone();
+
+ // Add a database to the set of canned databases that is returned by this
+ // helper.
+ void AddDatabase(const GURL& origin,
+ const std::string& name,
+ const std::string& description);
+
+ // Clear the list of canned databases.
+ void Reset();
+
+ // True if no databases are currently stored.
+ bool empty() const;
+
+ // Returns the number of currently stored databases.
+ size_t GetDatabaseCount() const;
+
+ // Returns the current list of web databases.
+ const std::set<PendingDatabaseInfo>& GetPendingDatabaseInfo();
+
+ // BrowsingDataDatabaseHelper implementation.
+ virtual void StartFetching(
+ const base::Callback<void(const std::list<DatabaseInfo>&)>& callback)
+ OVERRIDE;
+
+ private:
+ virtual ~CannedBrowsingDataDatabaseHelper();
+
+ // Converts the pending database info structs to database info structs.
+ void ConvertInfoInWebKitThread();
+
+ // Used to protect access to pending_database_info_.
+ mutable base::Lock lock_;
+
+ // Access to |pending_database_info_| is protected by |lock_| since it may
+ // be accessed on the UI or the WEBKIT thread.
+ std::set<PendingDatabaseInfo> pending_database_info_;
+
+ Profile* profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(CannedBrowsingDataDatabaseHelper);
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_DATABASE_HELPER_H_
diff --git a/chrome/browser/browsing_data/browsing_data_database_helper_browsertest.cc b/chrome/browser/browsing_data/browsing_data_database_helper_browsertest.cc
new file mode 100644
index 0000000..80017b5
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_database_helper_browsertest.cc
@@ -0,0 +1,151 @@
+// Copyright (c) 2012 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 "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/file_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browsing_data/browsing_data_database_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_helper_browsertest.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/test/test_browser_thread.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+
+namespace {
+typedef BrowsingDataHelperCallback<BrowsingDataDatabaseHelper::DatabaseInfo>
+ TestCompletionCallback;
+
+const char kTestIdentifier1[] = "http_www.google.com_0";
+
+const char kTestIdentifierExtension[] =
+ "chrome-extension_behllobkkfkfnphdnhnkndlbkcpglgmj_0";
+
+class BrowsingDataDatabaseHelperTest : public InProcessBrowserTest {
+ public:
+ virtual void CreateDatabases() {
+ webkit_database::DatabaseTracker* db_tracker =
+ BrowserContext::GetDatabaseTracker(browser()->profile());
+ string16 db_name = ASCIIToUTF16("db");
+ string16 description = ASCIIToUTF16("db_description");
+ int64 size;
+ string16 identifier1(UTF8ToUTF16(kTestIdentifier1));
+ db_tracker->DatabaseOpened(identifier1, db_name, description, 1, &size);
+ db_tracker->DatabaseClosed(identifier1, db_name);
+ FilePath db_path1 = db_tracker->GetFullDBFilePath(identifier1, db_name);
+ file_util::CreateDirectory(db_path1.DirName());
+ ASSERT_EQ(0, file_util::WriteFile(db_path1, NULL, 0));
+ string16 identifierExtension(UTF8ToUTF16(kTestIdentifierExtension));
+ db_tracker->DatabaseOpened(identifierExtension, db_name, description, 1,
+ &size);
+ db_tracker->DatabaseClosed(identifierExtension, db_name);
+ FilePath db_path2 =
+ db_tracker->GetFullDBFilePath(identifierExtension, db_name);
+ file_util::CreateDirectory(db_path2.DirName());
+ ASSERT_EQ(0, file_util::WriteFile(db_path2, NULL, 0));
+ std::vector<webkit_database::OriginInfo> origins;
+ db_tracker->GetAllOriginsInfo(&origins);
+ ASSERT_EQ(2U, origins.size());
+ }
+};
+
+// Called back by BrowsingDataDatabaseHelper on the UI thread once the database
+// information has been retrieved.
+class StopTestOnCallback {
+ public:
+ explicit StopTestOnCallback(
+ BrowsingDataDatabaseHelper* database_helper)
+ : database_helper_(database_helper) {
+ DCHECK(database_helper_);
+ }
+
+ void Callback(const std::list<BrowsingDataDatabaseHelper::DatabaseInfo>&
+ database_info_list) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ ASSERT_EQ(1UL, database_info_list.size());
+ EXPECT_EQ(std::string(kTestIdentifier1),
+ database_info_list.begin()->origin_identifier);
+ MessageLoop::current()->Quit();
+ }
+
+ private:
+ BrowsingDataDatabaseHelper* database_helper_;
+};
+
+// Flaky on Win/Mac/Linux: http://crbug.com/92460
+IN_PROC_BROWSER_TEST_F(BrowsingDataDatabaseHelperTest, DISABLED_FetchData) {
+ CreateDatabases();
+ scoped_refptr<BrowsingDataDatabaseHelper> database_helper(
+ new BrowsingDataDatabaseHelper(browser()->profile()));
+ StopTestOnCallback stop_test_on_callback(database_helper);
+ database_helper->StartFetching(
+ base::Bind(&StopTestOnCallback::Callback,
+ base::Unretained(&stop_test_on_callback)));
+ // Blocks until StopTestOnCallback::Callback is notified.
+ ui_test_utils::RunMessageLoop();
+}
+
+IN_PROC_BROWSER_TEST_F(BrowsingDataDatabaseHelperTest, CannedAddDatabase) {
+ const GURL origin1("http://host1:1/");
+ const GURL origin2("http://host2:1/");
+ const char origin_str1[] = "http_host1_1";
+ const char origin_str2[] = "http_host2_1";
+ const char db1[] = "db1";
+ const char db2[] = "db2";
+ const char db3[] = "db3";
+
+ scoped_refptr<CannedBrowsingDataDatabaseHelper> helper(
+ new CannedBrowsingDataDatabaseHelper(browser()->profile()));
+ helper->AddDatabase(origin1, db1, "");
+ helper->AddDatabase(origin1, db2, "");
+ helper->AddDatabase(origin2, db3, "");
+
+ TestCompletionCallback callback;
+ helper->StartFetching(
+ base::Bind(&TestCompletionCallback::callback,
+ base::Unretained(&callback)));
+
+ std::list<BrowsingDataDatabaseHelper::DatabaseInfo> result =
+ callback.result();
+
+ ASSERT_EQ(3u, result.size());
+ std::list<BrowsingDataDatabaseHelper::DatabaseInfo>::iterator info =
+ result.begin();
+ EXPECT_STREQ(origin_str1, info->origin_identifier.c_str());
+ EXPECT_STREQ(db1, info->database_name.c_str());
+ info++;
+ EXPECT_STREQ(origin_str1, info->origin_identifier.c_str());
+ EXPECT_STREQ(db2, info->database_name.c_str());
+ info++;
+ EXPECT_STREQ(origin_str2, info->origin_identifier.c_str());
+ EXPECT_STREQ(db3, info->database_name.c_str());
+}
+
+IN_PROC_BROWSER_TEST_F(BrowsingDataDatabaseHelperTest, CannedUnique) {
+ const GURL origin("http://host1:1/");
+ const char origin_str[] = "http_host1_1";
+ const char db[] = "db1";
+
+ scoped_refptr<CannedBrowsingDataDatabaseHelper> helper(
+ new CannedBrowsingDataDatabaseHelper(browser()->profile()));
+ helper->AddDatabase(origin, db, "");
+ helper->AddDatabase(origin, db, "");
+
+ TestCompletionCallback callback;
+ helper->StartFetching(
+ base::Bind(&TestCompletionCallback::callback,
+ base::Unretained(&callback)));
+
+ std::list<BrowsingDataDatabaseHelper::DatabaseInfo> result =
+ callback.result();
+
+ ASSERT_EQ(1u, result.size());
+ EXPECT_STREQ(origin_str, result.begin()->origin_identifier.c_str());
+ EXPECT_STREQ(db, result.begin()->database_name.c_str());
+}
+} // namespace
diff --git a/chrome/browser/browsing_data/browsing_data_database_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_database_helper_unittest.cc
new file mode 100644
index 0000000..2a2b474
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_database_helper_unittest.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_database_helper.h"
+
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserThread;
+
+namespace {
+
+class CannedBrowsingDataDatabaseHelperTest : public testing::Test {
+ public:
+ CannedBrowsingDataDatabaseHelperTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_) {
+ }
+
+ protected:
+ MessageLoop message_loop_;
+ content::TestBrowserThread ui_thread_;
+};
+
+TEST_F(CannedBrowsingDataDatabaseHelperTest, Empty) {
+ TestingProfile profile;
+
+ const GURL origin("http://host1:1/");
+ const char db[] = "db1";
+
+ scoped_refptr<CannedBrowsingDataDatabaseHelper> helper(
+ new CannedBrowsingDataDatabaseHelper(&profile));
+
+ ASSERT_TRUE(helper->empty());
+ helper->AddDatabase(origin, db, "");
+ ASSERT_FALSE(helper->empty());
+ helper->Reset();
+ ASSERT_TRUE(helper->empty());
+}
+
+TEST_F(CannedBrowsingDataDatabaseHelperTest, IgnoreExtensionsAndDevTools) {
+ TestingProfile profile;
+
+ const GURL origin1("chrome-extension://abcdefghijklmnopqrstuvwxyz/");
+ const GURL origin2("chrome-devtools://abcdefghijklmnopqrstuvwxyz/");
+ const char db[] = "db1";
+
+ scoped_refptr<CannedBrowsingDataDatabaseHelper> helper(
+ new CannedBrowsingDataDatabaseHelper(&profile));
+
+ ASSERT_TRUE(helper->empty());
+ helper->AddDatabase(origin1, db, "");
+ ASSERT_TRUE(helper->empty());
+ helper->AddDatabase(origin2, db, "");
+ ASSERT_TRUE(helper->empty());
+ helper->Reset();
+ ASSERT_TRUE(helper->empty());
+}
+
+} // namespace
diff --git a/chrome/browser/browsing_data/browsing_data_file_system_helper.cc b/chrome/browser/browsing_data/browsing_data_file_system_helper.cc
new file mode 100644
index 0000000..c1bd432
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_file_system_helper.cc
@@ -0,0 +1,283 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_file_system_helper.h"
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/file_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_thread.h"
+#include "webkit/fileapi/file_system_context.h"
+#include "webkit/fileapi/file_system_quota_util.h"
+#include "webkit/fileapi/file_system_types.h"
+#include "webkit/fileapi/sandbox_mount_point_provider.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+
+namespace {
+
+// An implementation of the BrowsingDataFileSystemHelper interface that pulls
+// data from a given |profile| and returns a list of FileSystemInfo items to a
+// client.
+class BrowsingDataFileSystemHelperImpl : public BrowsingDataFileSystemHelper {
+ public:
+ // BrowsingDataFileSystemHelper implementation
+ explicit BrowsingDataFileSystemHelperImpl(Profile* profile);
+ virtual void StartFetching(const base::Callback<
+ void(const std::list<FileSystemInfo>&)>& callback) OVERRIDE;
+ virtual void DeleteFileSystemOrigin(const GURL& origin) OVERRIDE;
+
+ private:
+ virtual ~BrowsingDataFileSystemHelperImpl();
+
+ // Enumerates all filesystem files, storing the resulting list into
+ // file_system_file_ for later use. This must be called on the FILE thread.
+ void FetchFileSystemInfoInFileThread();
+
+ // Triggers the success callback as the end of a StartFetching workflow. This
+ // must be called on the UI thread.
+ void NotifyOnUIThread();
+
+ // Deletes all file systems associated with |origin|. This must be called on
+ // the FILE thread.
+ void DeleteFileSystemOriginInFileThread(const GURL& origin);
+
+ // Keep a reference to the FileSystemContext object for the current profile
+ // for use on the FILE thread.
+ scoped_refptr<fileapi::FileSystemContext> filesystem_context_;
+
+ // Holds the current list of file systems returned to the client after
+ // StartFetching is called. Access to |file_system_info_| is triggered
+ // indirectly via the UI thread and guarded by |is_fetching_|. This means
+ // |file_system_info_| is only accessed while |is_fetching_| is true. The
+ // flag |is_fetching_| is only accessed on the UI thread. In the context of
+ // this class |file_system_info_| only mutates on the FILE thread.
+ std::list<FileSystemInfo> file_system_info_;
+
+ // Holds the callback passed in at the beginning of the StartFetching workflow
+ // so that it can be triggered via NotifyOnUIThread. This only mutates on the
+ // UI thread.
+ base::Callback<void(const std::list<FileSystemInfo>&)> completion_callback_;
+
+ // Indicates whether or not we're currently fetching information: set to true
+ // when StartFetching is called on the UI thread, and reset to false when
+ // NotifyOnUIThread triggers the success callback.
+ // This property only mutates on the UI thread.
+ bool is_fetching_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataFileSystemHelperImpl);
+};
+
+BrowsingDataFileSystemHelperImpl::BrowsingDataFileSystemHelperImpl(
+ Profile* profile)
+ : filesystem_context_(BrowserContext::GetFileSystemContext(profile)),
+ is_fetching_(false) {
+ DCHECK(filesystem_context_);
+}
+
+BrowsingDataFileSystemHelperImpl::~BrowsingDataFileSystemHelperImpl() {
+}
+
+void BrowsingDataFileSystemHelperImpl::StartFetching(
+ const base::Callback<void(const std::list<FileSystemInfo>&)>& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!is_fetching_);
+ DCHECK_EQ(false, callback.is_null());
+ is_fetching_ = true;
+ completion_callback_ = callback;
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(
+ &BrowsingDataFileSystemHelperImpl::FetchFileSystemInfoInFileThread,
+ this));
+}
+
+void BrowsingDataFileSystemHelperImpl::DeleteFileSystemOrigin(
+ const GURL& origin) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(
+ &BrowsingDataFileSystemHelperImpl::DeleteFileSystemOriginInFileThread,
+ this, origin));
+}
+
+void BrowsingDataFileSystemHelperImpl::FetchFileSystemInfoInFileThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ scoped_ptr<fileapi::SandboxMountPointProvider::OriginEnumerator>
+ origin_enumerator(filesystem_context_->
+ sandbox_provider()->CreateOriginEnumerator());
+
+ // We don't own this pointer; it's a magic singleton generated by the
+ // profile's FileSystemContext. Deleting it would be a bad idea.
+ fileapi::FileSystemQuotaUtil* quota_util =
+ filesystem_context_->GetQuotaUtil(fileapi::kFileSystemTypeTemporary);
+
+ GURL current;
+
+ while (!(current = origin_enumerator->Next()).is_empty()) {
+ if (!BrowsingDataHelper::HasWebScheme(current))
+ continue; // Non-websafe state is not considered browsing data.
+
+ // We can call these synchronous methods as we've already verified that
+ // we're running on the FILE thread.
+ int64 persistent_usage = quota_util->GetOriginUsageOnFileThread(
+ filesystem_context_, current,
+ fileapi::kFileSystemTypePersistent);
+ int64 temporary_usage = quota_util->GetOriginUsageOnFileThread(
+ filesystem_context_, current,
+ fileapi::kFileSystemTypeTemporary);
+ file_system_info_.push_back(
+ FileSystemInfo(
+ current,
+ origin_enumerator->HasFileSystemType(
+ fileapi::kFileSystemTypePersistent),
+ origin_enumerator->HasFileSystemType(
+ fileapi::kFileSystemTypeTemporary),
+ persistent_usage,
+ temporary_usage));
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&BrowsingDataFileSystemHelperImpl::NotifyOnUIThread, this));
+}
+
+void BrowsingDataFileSystemHelperImpl::NotifyOnUIThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(is_fetching_);
+ completion_callback_.Run(file_system_info_);
+ completion_callback_.Reset();
+ is_fetching_ = false;
+}
+
+void BrowsingDataFileSystemHelperImpl::DeleteFileSystemOriginInFileThread(
+ const GURL& origin) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ filesystem_context_->DeleteDataForOriginOnFileThread(origin);
+}
+
+} // namespace
+
+BrowsingDataFileSystemHelper::FileSystemInfo::FileSystemInfo(
+ const GURL& origin,
+ bool has_persistent,
+ bool has_temporary,
+ int64 usage_persistent,
+ int64 usage_temporary)
+ : origin(origin),
+ has_persistent(has_persistent),
+ has_temporary(has_temporary),
+ usage_persistent(usage_persistent),
+ usage_temporary(usage_temporary) {
+}
+
+BrowsingDataFileSystemHelper::FileSystemInfo::~FileSystemInfo() {}
+
+// static
+BrowsingDataFileSystemHelper* BrowsingDataFileSystemHelper::Create(
+ Profile* profile) {
+ return new BrowsingDataFileSystemHelperImpl(profile);
+}
+
+CannedBrowsingDataFileSystemHelper::CannedBrowsingDataFileSystemHelper(
+ Profile* profile)
+ : is_fetching_(false) {
+}
+
+CannedBrowsingDataFileSystemHelper::CannedBrowsingDataFileSystemHelper()
+ : is_fetching_(false) {
+}
+
+CannedBrowsingDataFileSystemHelper::~CannedBrowsingDataFileSystemHelper() {}
+
+CannedBrowsingDataFileSystemHelper*
+ CannedBrowsingDataFileSystemHelper::Clone() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ CannedBrowsingDataFileSystemHelper* clone =
+ new CannedBrowsingDataFileSystemHelper();
+ // This list only mutates on the UI thread, so it's safe to work with it here
+ // (given the DCHECK above).
+ clone->file_system_info_ = file_system_info_;
+ return clone;
+}
+
+void CannedBrowsingDataFileSystemHelper::AddFileSystem(
+ const GURL& origin, const fileapi::FileSystemType type, const int64 size) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // This canned implementation of AddFileSystem uses an O(n^2) algorithm; which
+ // is fine, as it isn't meant for use in a high-volume context. If it turns
+ // out that we want to start using this in a context with many, many origins,
+ // we should think about reworking the implementation.
+ bool duplicate_origin = false;
+ for (std::list<FileSystemInfo>::iterator
+ file_system = file_system_info_.begin();
+ file_system != file_system_info_.end();
+ ++file_system) {
+ if (file_system->origin == origin) {
+ if (type == fileapi::kFileSystemTypePersistent) {
+ file_system->has_persistent = true;
+ file_system->usage_persistent = size;
+ } else {
+ file_system->has_temporary = true;
+ file_system->usage_temporary = size;
+ }
+ duplicate_origin = true;
+ break;
+ }
+ }
+ if (duplicate_origin)
+ return;
+
+ if (!BrowsingDataHelper::HasWebScheme(origin))
+ return; // Non-websafe state is not considered browsing data.
+
+ file_system_info_.push_back(FileSystemInfo(
+ origin,
+ (type == fileapi::kFileSystemTypePersistent),
+ (type == fileapi::kFileSystemTypeTemporary),
+ (type == fileapi::kFileSystemTypePersistent) ? size : 0,
+ (type == fileapi::kFileSystemTypeTemporary) ? size : 0));
+}
+
+void CannedBrowsingDataFileSystemHelper::Reset() {
+ file_system_info_.clear();
+}
+
+bool CannedBrowsingDataFileSystemHelper::empty() const {
+ return file_system_info_.empty();
+}
+
+size_t CannedBrowsingDataFileSystemHelper::GetFileSystemCount() const {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return file_system_info_.size();
+}
+
+void CannedBrowsingDataFileSystemHelper::StartFetching(
+ const base::Callback<void(const std::list<FileSystemInfo>&)>& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!is_fetching_);
+ DCHECK_EQ(false, callback.is_null());
+ is_fetching_ = true;
+ completion_callback_ = callback;
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&CannedBrowsingDataFileSystemHelper::NotifyOnUIThread, this));
+}
+
+void CannedBrowsingDataFileSystemHelper::NotifyOnUIThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(is_fetching_);
+ completion_callback_.Run(file_system_info_);
+ completion_callback_.Reset();
+ is_fetching_ = false;
+}
diff --git a/chrome/browser/browsing_data/browsing_data_file_system_helper.h b/chrome/browser/browsing_data/browsing_data_file_system_helper.h
new file mode 100644
index 0000000..89d685c
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_file_system_helper.h
@@ -0,0 +1,178 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_FILE_SYSTEM_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_FILE_SYSTEM_HELPER_H_
+
+#include <list>
+#include <string>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/time.h"
+#include "chrome/common/url_constants.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/fileapi/file_system_types.h"
+
+class Profile;
+
+// Defines an interface for classes that deal with aggregating and deleting
+// browsing data stored in an origin's file systems.
+// BrowsingDataFileSystemHelper instances for a specific profile should be
+// created via the static Create method. Each instance will lazily fetch file
+// system data when a client calls StartFetching from the UI thread, and will
+// notify the client via a supplied callback when the data is available.
+// Only one StartFetching task can run at a time: executing StartFetching while
+// another StartFetching task is running will DCHECK.
+//
+// The client's callback is passed a list of FileSystemInfo objects containing
+// usage information for each origin's temporary and persistent file systems.
+//
+// Clients may remove an origin's file systems at any time (even before fetching
+// data) by calling DeleteFileSystemOrigin() on the UI thread. Calling
+// DeleteFileSystemOrigin() for an origin that doesn't have any is safe; it's
+// just an expensive NOOP.
+class BrowsingDataFileSystemHelper
+ : public base::RefCountedThreadSafe<BrowsingDataFileSystemHelper> {
+ public:
+ // Detailed information about a file system, including it's origin GURL,
+ // the presence or absence of persistent and temporary storage, and the
+ // amount of data (in bytes) each contains.
+ struct FileSystemInfo {
+ FileSystemInfo(
+ const GURL& origin,
+ bool has_persistent,
+ bool has_temporary,
+ int64 usage_persistent,
+ int64 usage_temporary);
+ ~FileSystemInfo();
+
+ // The origin for which the information is relevant.
+ GURL origin;
+ // True if the origin has a persistent file system.
+ bool has_persistent;
+ // True if the origin has a temporary file system.
+ bool has_temporary;
+ // Persistent file system usage, in bytes.
+ int64 usage_persistent;
+ // Temporary file system usage, in bytes.
+ int64 usage_temporary;
+ };
+
+ // Creates a BrowsingDataFileSystemHelper instance for the file systems
+ // stored in |profile|'s user data directory. The BrowsingDataFileSystemHelper
+ // object will hold a reference to the Profile that's passed in, but is not
+ // responsible for destroying it.
+ //
+ // The BrowsingDataFileSystemHelper will not change the profile itself, but
+ // can modify data it contains (by removing file systems).
+ static BrowsingDataFileSystemHelper* Create(Profile* profile);
+
+ // Starts the process of fetching file system data, which will call |callback|
+ // upon completion, passing it a constant list of FileSystemInfo objects.
+ // StartFetching must be called only in the UI thread; the provided Callback1
+ // will likewise be executed asynchronously on the UI thread.
+ //
+ // BrowsingDataFileSystemHelper takes ownership of the Callback1, and is
+ // responsible for deleting it once it's no longer needed.
+ virtual void StartFetching(const base::Callback<
+ void(const std::list<FileSystemInfo>&)>& callback) = 0;
+
+ // Deletes any temporary or persistent file systems associated with |origin|
+ // from the disk. Deletion will occur asynchronously on the FILE thread, but
+ // this function must be called only on the UI thread.
+ virtual void DeleteFileSystemOrigin(const GURL& origin) = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<BrowsingDataFileSystemHelper>;
+
+ BrowsingDataFileSystemHelper() {}
+ virtual ~BrowsingDataFileSystemHelper() {}
+};
+
+// An implementation of the BrowsingDataFileSystemHelper interface that can
+// be manually populated with data, rather than fetching data from the file
+// systems created in a particular Profile.
+class CannedBrowsingDataFileSystemHelper
+ : public BrowsingDataFileSystemHelper {
+ public:
+ // |profile| is unused in this canned implementation, but it's the interface
+ // we're writing to, so we'll accept it, but not store it.
+ explicit CannedBrowsingDataFileSystemHelper(Profile* profile);
+
+ // Creates a copy of the file system helper. StartFetching can only respond
+ // to one client at a time; we need to be able to act on multiple parallel
+ // requests in certain situations (see CookiesTreeModel and its clients). For
+ // these cases, simply clone the object and fire off another fetching process.
+ //
+ // Clone() is safe to call while StartFetching() is running. Clients of the
+ // newly created object must themselves execute StartFetching(), however: the
+ // copy will not have a pending fetch.
+ CannedBrowsingDataFileSystemHelper* Clone();
+
+ // Manually adds a filesystem to the set of canned file systems that this
+ // helper returns via StartFetching. If an origin contains both a temporary
+ // and a persistent filesystem, AddFileSystem must be called twice (once for
+ // each file system type).
+ void AddFileSystem(const GURL& origin,
+ fileapi::FileSystemType type,
+ int64 size);
+
+ // Clear this helper's list of canned filesystems.
+ void Reset();
+
+ // True if no filesystems are currently stored.
+ bool empty() const;
+
+ // Returns the number of currently stored filesystems.
+ size_t GetFileSystemCount() const;
+
+ // Returns the current list of filesystems.
+ const std::list<FileSystemInfo>& GetFileSystemInfo() {
+ return file_system_info_;
+ }
+
+ // BrowsingDataFileSystemHelper implementation.
+ virtual void StartFetching(const base::Callback<
+ void(const std::list<FileSystemInfo>&)>& callback) OVERRIDE;
+
+ // Note that this doesn't actually have an implementation for this canned
+ // class. It hasn't been necessary for anything that uses the canned
+ // implementation, as the canned class is only used in tests, or in read-only
+ // contexts (like the non-modal cookie dialog).
+ virtual void DeleteFileSystemOrigin(const GURL& origin) OVERRIDE {}
+
+ private:
+ // Used by Clone() to create an object without a Profile
+ CannedBrowsingDataFileSystemHelper();
+ virtual ~CannedBrowsingDataFileSystemHelper();
+
+ // Triggers the success callback as the end of a StartFetching workflow. This
+ // must be called on the UI thread.
+ void NotifyOnUIThread();
+
+ // Holds the current list of filesystems returned to the client. Access to
+ // |file_system_info_| is triggered indirectly via the UI thread and guarded
+ // by |is_fetching_|. This means |file_system_info_| is only accessed while
+ // |is_fetching_| is true. The flag |is_fetching_| is only accessed on the UI
+ // thread.
+ std::list<FileSystemInfo> file_system_info_;
+
+ // The callback passed in at the beginning of the StartFetching workflow so
+ // that it can be triggered via NotifyOnUIThread.
+ base::Callback<void(const std::list<FileSystemInfo>&)> completion_callback_;
+
+ // Indicates whether or not we're currently fetching information: set to true
+ // when StartFetching is called on the UI thread, and reset to false when
+ // NotifyOnUIThread triggers the success callback.
+ // This property only mutates on the UI thread.
+ bool is_fetching_;
+
+ DISALLOW_COPY_AND_ASSIGN(CannedBrowsingDataFileSystemHelper);
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_FILE_SYSTEM_HELPER_H_
diff --git a/chrome/browser/browsing_data/browsing_data_file_system_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_file_system_helper_unittest.cc
new file mode 100644
index 0000000..d60e8f2
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_file_system_helper_unittest.cc
@@ -0,0 +1,318 @@
+// Copyright (c) 2012 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 "testing/gtest/include/gtest/gtest.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/file_util.h"
+#include "base/platform_file.h"
+#include "base/message_loop.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browsing_data/browsing_data_file_system_helper.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread.h"
+#include "webkit/fileapi/file_system_context.h"
+#include "webkit/fileapi/file_system_types.h"
+#include "webkit/fileapi/file_system_usage_cache.h"
+#include "webkit/fileapi/sandbox_mount_point_provider.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+
+namespace {
+
+// Shorter names for fileapi::* constants.
+const fileapi::FileSystemType kTemporary = fileapi::kFileSystemTypeTemporary;
+const fileapi::FileSystemType kPersistent = fileapi::kFileSystemTypePersistent;
+
+// We'll use these three distinct origins for testing, both as strings and as
+// GURLs in appropriate contexts.
+const char kTestOrigin1[] = "http://host1:1/";
+const char kTestOrigin2[] = "http://host2:2/";
+const char kTestOrigin3[] = "http://host3:3/";
+
+// Extensions and Devtools should be ignored.
+const char kTestOriginExt[] = "chrome-extension://abcdefghijklmnopqrstuvwxyz/";
+const char kTestOriginDevTools[] = "chrome-devtools://abcdefghijklmnopqrstuvw/";
+
+const GURL kOrigin1(kTestOrigin1);
+const GURL kOrigin2(kTestOrigin2);
+const GURL kOrigin3(kTestOrigin3);
+const GURL kOriginExt(kTestOriginExt);
+const GURL kOriginDevTools(kTestOriginDevTools);
+
+// TODO(mkwst): Update this size once the discussion in http://crbug.com/86114
+// is concluded.
+const int kEmptyFileSystemSize = 0;
+
+typedef std::list<BrowsingDataFileSystemHelper::FileSystemInfo>
+ FileSystemInfoList;
+typedef scoped_ptr<FileSystemInfoList> ScopedFileSystemInfoList;
+
+// The FileSystem APIs are all asynchronous; this testing class wraps up the
+// boilerplate code necessary to deal with waiting for responses. In a nutshell,
+// any async call whose response we want to test ought to be followed by a call
+// to BlockUntilNotified(), which will (shockingly!) block until Notify() is
+// called. For this to work, you'll need to ensure that each async call is
+// implemented as a class method that that calls Notify() at an appropriate
+// point.
+class BrowsingDataFileSystemHelperTest : public testing::Test {
+ public:
+ BrowsingDataFileSystemHelperTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_),
+ db_thread_(BrowserThread::DB, &message_loop_),
+ webkit_thread_(BrowserThread::WEBKIT_DEPRECATED, &message_loop_),
+ file_thread_(BrowserThread::FILE, &message_loop_),
+ file_user_blocking_thread_(
+ BrowserThread::FILE_USER_BLOCKING, &message_loop_),
+ io_thread_(BrowserThread::IO, &message_loop_) {
+ profile_.reset(new TestingProfile());
+ helper_ = BrowsingDataFileSystemHelper::Create(profile_.get());
+ message_loop_.RunAllPending();
+ canned_helper_ = new CannedBrowsingDataFileSystemHelper(profile_.get());
+ }
+ virtual ~BrowsingDataFileSystemHelperTest() {
+ // Avoid memory leaks.
+ profile_.reset();
+ message_loop_.RunAllPending();
+ }
+
+ TestingProfile* GetProfile() {
+ return profile_.get();
+ }
+
+ // Blocks on the current MessageLoop until Notify() is called.
+ void BlockUntilNotified() {
+ MessageLoop::current()->Run();
+ }
+
+ // Unblocks the current MessageLoop. Should be called in response to some sort
+ // of async activity in a callback method.
+ void Notify() {
+ MessageLoop::current()->Quit();
+ }
+
+ // Callback that should be executed in response to
+ // fileapi::SandboxMountPointProvider::ValidateFileSystemRoot
+ void ValidateFileSystemCallback(base::PlatformFileError error) {
+ validate_file_system_result_ = error;
+ Notify();
+ }
+
+ // Calls fileapi::SandboxMountPointProvider::ValidateFileSystemRootAndGetURL
+ // to verify the existence of a file system for a specified type and origin,
+ // blocks until a response is available, then returns the result
+ // synchronously to it's caller.
+ bool FileSystemContainsOriginAndType(const GURL& origin,
+ fileapi::FileSystemType type) {
+ sandbox_->ValidateFileSystemRoot(
+ origin, type, false,
+ base::Bind(
+ &BrowsingDataFileSystemHelperTest::ValidateFileSystemCallback,
+ base::Unretained(this)));
+ BlockUntilNotified();
+ return validate_file_system_result_ == base::PLATFORM_FILE_OK;
+ }
+
+ // Callback that should be executed in response to StartFetching(), and stores
+ // found file systems locally so that they are available via GetFileSystems().
+ void CallbackStartFetching(
+ const std::list<BrowsingDataFileSystemHelper::FileSystemInfo>&
+ file_system_info_list) {
+ file_system_info_list_.reset(
+ new std::list<BrowsingDataFileSystemHelper::FileSystemInfo>(
+ file_system_info_list));
+ Notify();
+ }
+
+ // Calls StartFetching() on the test's BrowsingDataFileSystemHelper
+ // object, then blocks until the callback is executed.
+ void FetchFileSystems() {
+ helper_->StartFetching(
+ base::Bind(&BrowsingDataFileSystemHelperTest::CallbackStartFetching,
+ base::Unretained(this)));
+ BlockUntilNotified();
+ }
+
+ // Calls StartFetching() on the test's CannedBrowsingDataFileSystemHelper
+ // object, then blocks until the callback is executed.
+ void FetchCannedFileSystems() {
+ canned_helper_->StartFetching(
+ base::Bind(&BrowsingDataFileSystemHelperTest::CallbackStartFetching,
+ base::Unretained(this)));
+ BlockUntilNotified();
+ }
+
+ // Sets up kOrigin1 with a temporary file system, kOrigin2 with a persistent
+ // file system, and kOrigin3 with both.
+ virtual void PopulateTestFileSystemData() {
+ sandbox_ = BrowserContext::GetFileSystemContext(profile_.get())->
+ sandbox_provider();
+
+ CreateDirectoryForOriginAndType(kOrigin1, kTemporary);
+ CreateDirectoryForOriginAndType(kOrigin2, kPersistent);
+ CreateDirectoryForOriginAndType(kOrigin3, kTemporary);
+ CreateDirectoryForOriginAndType(kOrigin3, kPersistent);
+
+ EXPECT_FALSE(FileSystemContainsOriginAndType(kOrigin1, kPersistent));
+ EXPECT_TRUE(FileSystemContainsOriginAndType(kOrigin1, kTemporary));
+ EXPECT_TRUE(FileSystemContainsOriginAndType(kOrigin2, kPersistent));
+ EXPECT_FALSE(FileSystemContainsOriginAndType(kOrigin2, kTemporary));
+ EXPECT_TRUE(FileSystemContainsOriginAndType(kOrigin3, kPersistent));
+ EXPECT_TRUE(FileSystemContainsOriginAndType(kOrigin3, kTemporary));
+ }
+
+ // Uses the fileapi methods to create a filesystem of a given type for a
+ // specified origin.
+ void CreateDirectoryForOriginAndType(const GURL& origin,
+ fileapi::FileSystemType type) {
+ FilePath target = sandbox_->GetFileSystemRootPathOnFileThread(
+ origin, type, FilePath(), true);
+ EXPECT_TRUE(file_util::DirectoryExists(target));
+ }
+
+ // Returns a list of the FileSystemInfo objects gathered in the most recent
+ // call to StartFetching().
+ FileSystemInfoList* GetFileSystems() {
+ return file_system_info_list_.get();
+ }
+
+
+ // Temporary storage to pass information back from callbacks.
+ base::PlatformFileError validate_file_system_result_;
+ ScopedFileSystemInfoList file_system_info_list_;
+
+ scoped_refptr<BrowsingDataFileSystemHelper> helper_;
+ scoped_refptr<CannedBrowsingDataFileSystemHelper> canned_helper_;
+
+ private:
+ // message_loop_, as well as all the threads associated with it must be
+ // defined before profile_ to prevent explosions. The threads also must be
+ // defined in the order they're listed here. Oh how I love C++.
+ MessageLoopForUI message_loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread db_thread_;
+ content::TestBrowserThread webkit_thread_;
+ content::TestBrowserThread file_thread_;
+ content::TestBrowserThread file_user_blocking_thread_;
+ content::TestBrowserThread io_thread_;
+ scoped_ptr<TestingProfile> profile_;
+
+ // We don't own this pointer: don't delete it.
+ fileapi::SandboxMountPointProvider* sandbox_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataFileSystemHelperTest);
+};
+
+// Verifies that the BrowsingDataFileSystemHelper correctly finds the test file
+// system data, and that each file system returned contains the expected data.
+TEST_F(BrowsingDataFileSystemHelperTest, FetchData) {
+ PopulateTestFileSystemData();
+
+ FetchFileSystems();
+
+ EXPECT_EQ(3UL, file_system_info_list_->size());
+
+ // Order is arbitrary, verify all three origins.
+ bool test_hosts_found[3] = {false, false, false};
+ for (std::list<BrowsingDataFileSystemHelper::FileSystemInfo>::iterator info =
+ file_system_info_list_->begin(); info != file_system_info_list_->end();
+ ++info) {
+ if (info->origin == kOrigin1) {
+ EXPECT_FALSE(test_hosts_found[0]);
+ test_hosts_found[0] = true;
+ EXPECT_FALSE(info->has_persistent);
+ EXPECT_TRUE(info->has_temporary);
+ EXPECT_EQ(0, info->usage_persistent);
+ EXPECT_EQ(kEmptyFileSystemSize, info->usage_temporary);
+ } else if (info->origin == kOrigin2) {
+ EXPECT_FALSE(test_hosts_found[1]);
+ test_hosts_found[1] = true;
+ EXPECT_TRUE(info->has_persistent);
+ EXPECT_FALSE(info->has_temporary);
+ EXPECT_EQ(kEmptyFileSystemSize, info->usage_persistent);
+ EXPECT_EQ(0, info->usage_temporary);
+ } else if (info->origin == kOrigin3) {
+ EXPECT_FALSE(test_hosts_found[2]);
+ test_hosts_found[2] = true;
+ EXPECT_TRUE(info->has_persistent);
+ EXPECT_TRUE(info->has_temporary);
+ EXPECT_EQ(kEmptyFileSystemSize, info->usage_persistent);
+ EXPECT_EQ(kEmptyFileSystemSize, info->usage_temporary);
+ } else {
+ ADD_FAILURE() << info->origin.spec() << " isn't an origin we added.";
+ }
+ }
+ for (size_t i = 0; i < arraysize(test_hosts_found); i++) {
+ EXPECT_TRUE(test_hosts_found[i]);
+ }
+}
+
+// Verifies that the BrowsingDataFileSystemHelper correctly deletes file
+// systems via DeleteFileSystemOrigin().
+TEST_F(BrowsingDataFileSystemHelperTest, DeleteData) {
+ PopulateTestFileSystemData();
+
+ helper_->DeleteFileSystemOrigin(kOrigin1);
+ helper_->DeleteFileSystemOrigin(kOrigin2);
+
+ FetchFileSystems();
+
+ EXPECT_EQ(1UL, file_system_info_list_->size());
+ BrowsingDataFileSystemHelper::FileSystemInfo info =
+ *(file_system_info_list_->begin());
+ EXPECT_EQ(kOrigin3, info.origin);
+ EXPECT_TRUE(info.has_persistent);
+ EXPECT_TRUE(info.has_temporary);
+ EXPECT_EQ(kEmptyFileSystemSize, info.usage_persistent);
+ EXPECT_EQ(kEmptyFileSystemSize, info.usage_temporary);
+}
+
+// Verifies that the CannedBrowsingDataFileSystemHelper correctly reports
+// whether or not it currently contains file systems.
+TEST_F(BrowsingDataFileSystemHelperTest, Empty) {
+ ASSERT_TRUE(canned_helper_->empty());
+ canned_helper_->AddFileSystem(kOrigin1, kTemporary, 0);
+ ASSERT_FALSE(canned_helper_->empty());
+ canned_helper_->Reset();
+ ASSERT_TRUE(canned_helper_->empty());
+}
+
+// Verifies that AddFileSystem correctly adds file systems, and that both
+// the type and usage metadata are reported as provided.
+TEST_F(BrowsingDataFileSystemHelperTest, CannedAddFileSystem) {
+ canned_helper_->AddFileSystem(kOrigin1, kPersistent, 200);
+ canned_helper_->AddFileSystem(kOrigin2, kTemporary, 100);
+
+ FetchCannedFileSystems();
+
+ EXPECT_EQ(2U, file_system_info_list_->size());
+ std::list<BrowsingDataFileSystemHelper::FileSystemInfo>::iterator info =
+ file_system_info_list_->begin();
+ EXPECT_EQ(kOrigin1, info->origin);
+ EXPECT_TRUE(info->has_persistent);
+ EXPECT_FALSE(info->has_temporary);
+ EXPECT_EQ(200, info->usage_persistent);
+ EXPECT_EQ(0, info->usage_temporary);
+
+ info++;
+ EXPECT_EQ(kOrigin2, info->origin);
+ EXPECT_FALSE(info->has_persistent);
+ EXPECT_TRUE(info->has_temporary);
+ EXPECT_EQ(0, info->usage_persistent);
+ EXPECT_EQ(100, info->usage_temporary);
+}
+
+// Verifies that the CannedBrowsingDataFileSystemHelper correctly ignores
+// extension and devtools schemes.
+TEST_F(BrowsingDataFileSystemHelperTest, IgnoreExtensionsAndDevTools) {
+ ASSERT_TRUE(canned_helper_->empty());
+ canned_helper_->AddFileSystem(kOriginExt, kTemporary, 0);
+ ASSERT_TRUE(canned_helper_->empty());
+ canned_helper_->AddFileSystem(kOriginDevTools, kTemporary, 0);
+ ASSERT_TRUE(canned_helper_->empty());
+}
+
+} // namespace
diff --git a/chrome/browser/browsing_data/browsing_data_helper.cc b/chrome/browser/browsing_data/browsing_data_helper.cc
new file mode 100644
index 0000000..18cdaa1
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_helper.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_helper.h"
+
+#include "base/command_line.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/extensions/extension_special_storage_policy.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/browser/child_process_security_policy.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
+
+// Static
+bool BrowsingDataHelper::IsWebScheme(const std::string& scheme) {
+ // Special-case `file://` scheme iff cookies and site data are enabled via
+ // the `--allow-file-cookies` CLI flag.
+ if (scheme == chrome::kFileScheme) {
+ return CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableFileCookies);
+
+ // Otherwise, all "web safe" schemes are valid, except `chrome-extension://`
+ // and `chrome-devtools://`.
+ } else {
+ content::ChildProcessSecurityPolicy* policy =
+ content::ChildProcessSecurityPolicy::GetInstance();
+ return (policy->IsWebSafeScheme(scheme) &&
+ !BrowsingDataHelper::IsExtensionScheme(scheme) &&
+ scheme != chrome::kChromeDevToolsScheme);
+ }
+}
+
+// Static
+bool BrowsingDataHelper::IsWebScheme(const WebKit::WebString& scheme) {
+ return BrowsingDataHelper::IsWebScheme(UTF16ToUTF8(scheme));
+}
+
+// Static
+bool BrowsingDataHelper::HasWebScheme(const GURL& origin) {
+ return BrowsingDataHelper::IsWebScheme(origin.scheme());
+}
+
+// Static
+bool BrowsingDataHelper::IsExtensionScheme(const std::string& scheme) {
+ return scheme == chrome::kExtensionScheme;
+}
+
+// Static
+bool BrowsingDataHelper::IsExtensionScheme(const WebKit::WebString& scheme) {
+ return BrowsingDataHelper::IsExtensionScheme(UTF16ToUTF8(scheme));
+}
+
+// Static
+bool BrowsingDataHelper::HasExtensionScheme(const GURL& origin) {
+ return BrowsingDataHelper::IsExtensionScheme(origin.scheme());
+}
+
+// Static
+bool BrowsingDataHelper::DoesOriginMatchMask(const GURL& origin,
+ int origin_set_mask, ExtensionSpecialStoragePolicy* policy) {
+ // Packaged apps and extensions match iff EXTENSION.
+ if (BrowsingDataHelper::HasExtensionScheme(origin.GetOrigin()) &&
+ origin_set_mask & EXTENSION)
+ return true;
+
+ // If a websafe origin is unprotected, it matches iff UNPROTECTED_WEB.
+ if (!policy->IsStorageProtected(origin.GetOrigin()) &&
+ BrowsingDataHelper::HasWebScheme(origin.GetOrigin()) &&
+ origin_set_mask & UNPROTECTED_WEB)
+ return true;
+
+ // Hosted applications (protected and websafe origins) iff PROTECTED_WEB.
+ if (policy->IsStorageProtected(origin.GetOrigin()) &&
+ BrowsingDataHelper::HasWebScheme(origin.GetOrigin()) &&
+ origin_set_mask & PROTECTED_WEB)
+ return true;
+
+ return false;
+}
diff --git a/chrome/browser/browsing_data/browsing_data_helper.h b/chrome/browser/browsing_data/browsing_data_helper.h
new file mode 100644
index 0000000..65afbcc
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_helper.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2012 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.
+
+// Defines methods relevant to all code that wants to work with browsing data.
+
+#ifndef CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_HELPER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace WebKit {
+class WebString;
+}
+
+class ExtensionSpecialStoragePolicy;
+class GURL;
+
+class BrowsingDataHelper {
+ public:
+ enum OriginSetMask {
+ UNPROTECTED_WEB = 1 << 0, // drive-by web.
+ PROTECTED_WEB = 1 << 1, // hosted applications.
+ EXTENSION = 1 << 2, // chrome-extension://*
+ // Always add new items to the enum above ALL and add them to ALL.
+ ALL = UNPROTECTED_WEB | PROTECTED_WEB | EXTENSION,
+ };
+
+ // Returns true iff the provided scheme is (really) web safe, and suitable
+ // for treatment as "browsing data". This relies on the definition of web safe
+ // in ChildProcessSecurityPolicy, but excluding schemes like
+ // `chrome-extension`.
+ static bool IsWebScheme(const std::string& scheme);
+ static bool IsWebScheme(const WebKit::WebString& scheme);
+ static bool HasWebScheme(const GURL& origin);
+
+ // Returns true iff the provided scheme is an extension.
+ static bool IsExtensionScheme(const std::string& scheme);
+ static bool IsExtensionScheme(const WebKit::WebString& scheme);
+ static bool HasExtensionScheme(const GURL& origin);
+
+ // Returns true if the provided origin matches the provided mask.
+ static bool DoesOriginMatchMask(const GURL& origin,
+ int origin_set_mask,
+ ExtensionSpecialStoragePolicy* policy);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(BrowsingDataHelper);
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_HELPER_H_
diff --git a/chrome/browser/browsing_data/browsing_data_helper_browsertest.h b/chrome/browser/browsing_data/browsing_data_helper_browsertest.h
new file mode 100644
index 0000000..1f953e3
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_helper_browsertest.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 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.
+
+// Contains code shared by all browsing data browsertests.
+
+#ifndef CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_HELPER_BROWSERTEST_H_
+#define CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_HELPER_BROWSERTEST_H_
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+
+// This template can be used for the StartFetching methods of the browsing data
+// helper classes. It is supposed to be instantiated with the respective
+// browsing data info type.
+template <typename T>
+class BrowsingDataHelperCallback {
+ public:
+ BrowsingDataHelperCallback()
+ : has_result_(false) {
+ }
+
+ const std::list<T>& result() {
+ MessageLoop::current()->Run();
+ DCHECK(has_result_);
+ return result_;
+ }
+
+ void callback(const std::list<T>& info) {
+ result_ = info;
+ has_result_ = true;
+ MessageLoop::current()->Quit();
+ }
+
+ private:
+ bool has_result_;
+ std::list<T> result_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataHelperCallback);
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_HELPER_BROWSERTEST_H_
diff --git a/chrome/browser/browsing_data/browsing_data_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_helper_unittest.cc
new file mode 100644
index 0000000..d3eab5d
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_helper_unittest.cc
@@ -0,0 +1,159 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_helper.h"
+
+#include "base/stringprintf.h"
+#include "chrome/browser/extensions/mock_extension_special_storage_policy.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/common/url_constants.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
+
+namespace {
+
+const char kTestOrigin1[] = "http://host1:1/";
+const char kTestOrigin2[] = "http://host2:1/";
+const char kTestOrigin3[] = "http://host3:1/";
+const char kTestOriginExt[] = "chrome-extension://abcdefghijklmnopqrstuvwxyz/";
+const char kTestOriginDevTools[] = "chrome-devtools://abcdefghijklmnopqrstuvw/";
+
+const GURL kOrigin1(kTestOrigin1);
+const GURL kOrigin2(kTestOrigin2);
+const GURL kOrigin3(kTestOrigin3);
+const GURL kOriginExt(kTestOriginExt);
+const GURL kOriginDevTools(kTestOriginDevTools);
+
+const int kExtension = BrowsingDataHelper::EXTENSION;
+const int kProtected = BrowsingDataHelper::PROTECTED_WEB;
+const int kUnprotected = BrowsingDataHelper::UNPROTECTED_WEB;
+
+class BrowsingDataHelperTest : public testing::Test {
+ public:
+ BrowsingDataHelperTest() {}
+ virtual ~BrowsingDataHelperTest() {}
+
+ bool IsWebScheme(const std::string& scheme) {
+ GURL test(scheme + "://example.com");
+ return (BrowsingDataHelper::HasWebScheme(test) &&
+ BrowsingDataHelper::IsWebScheme(scheme) &&
+ BrowsingDataHelper::IsWebScheme(
+ WebKit::WebString::fromUTF8(scheme)));
+ }
+
+ bool IsExtensionScheme(const std::string& scheme) {
+ GURL test(scheme + "://example.com");
+ return (BrowsingDataHelper::HasExtensionScheme(test) &&
+ BrowsingDataHelper::IsExtensionScheme(scheme) &&
+ BrowsingDataHelper::IsExtensionScheme(
+ WebKit::WebString::fromUTF8(scheme)));
+ }
+
+ bool Match(const GURL& origin,
+ int mask,
+ ExtensionSpecialStoragePolicy* policy) {
+ return BrowsingDataHelper::DoesOriginMatchMask(origin, mask, policy);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataHelperTest);
+};
+
+TEST_F(BrowsingDataHelperTest, WebSafeSchemesAreWebSafe) {
+ EXPECT_TRUE(IsWebScheme(chrome::kHttpScheme));
+ EXPECT_TRUE(IsWebScheme(chrome::kHttpsScheme));
+ EXPECT_TRUE(IsWebScheme(chrome::kFtpScheme));
+ EXPECT_TRUE(IsWebScheme(chrome::kDataScheme));
+ EXPECT_TRUE(IsWebScheme("feed"));
+ EXPECT_TRUE(IsWebScheme(chrome::kBlobScheme));
+ EXPECT_TRUE(IsWebScheme(chrome::kFileSystemScheme));
+ EXPECT_FALSE(IsWebScheme("invalid-scheme-i-just-made-up"));
+}
+
+TEST_F(BrowsingDataHelperTest, ChromeSchemesAreNotWebSafe) {
+ EXPECT_FALSE(IsWebScheme(chrome::kExtensionScheme));
+ EXPECT_FALSE(IsWebScheme(chrome::kAboutScheme));
+ EXPECT_FALSE(IsWebScheme(chrome::kChromeDevToolsScheme));
+ EXPECT_FALSE(IsWebScheme(chrome::kChromeInternalScheme));
+ EXPECT_FALSE(IsWebScheme(chrome::kChromeUIScheme));
+ EXPECT_FALSE(IsWebScheme(chrome::kJavaScriptScheme));
+ EXPECT_FALSE(IsWebScheme(chrome::kMailToScheme));
+ EXPECT_FALSE(IsWebScheme(chrome::kMetadataScheme));
+ EXPECT_FALSE(IsWebScheme(chrome::kSwappedOutScheme));
+ EXPECT_FALSE(IsWebScheme(chrome::kViewSourceScheme));
+}
+
+TEST_F(BrowsingDataHelperTest, WebSafeSchemesAreNotExtensions) {
+ EXPECT_FALSE(IsExtensionScheme(chrome::kHttpScheme));
+ EXPECT_FALSE(IsExtensionScheme(chrome::kHttpsScheme));
+ EXPECT_FALSE(IsExtensionScheme(chrome::kFtpScheme));
+ EXPECT_FALSE(IsExtensionScheme(chrome::kDataScheme));
+ EXPECT_FALSE(IsExtensionScheme("feed"));
+ EXPECT_FALSE(IsExtensionScheme(chrome::kBlobScheme));
+ EXPECT_FALSE(IsExtensionScheme(chrome::kFileSystemScheme));
+ EXPECT_FALSE(IsExtensionScheme("invalid-scheme-i-just-made-up"));
+}
+
+TEST_F(BrowsingDataHelperTest, ChromeSchemesAreNotAllExtension) {
+ EXPECT_TRUE(IsExtensionScheme(chrome::kExtensionScheme));
+
+ EXPECT_FALSE(IsExtensionScheme(chrome::kAboutScheme));
+ EXPECT_FALSE(IsExtensionScheme(chrome::kChromeDevToolsScheme));
+ EXPECT_FALSE(IsExtensionScheme(chrome::kChromeInternalScheme));
+ EXPECT_FALSE(IsExtensionScheme(chrome::kChromeUIScheme));
+ EXPECT_FALSE(IsExtensionScheme(chrome::kJavaScriptScheme));
+ EXPECT_FALSE(IsExtensionScheme(chrome::kMailToScheme));
+ EXPECT_FALSE(IsExtensionScheme(chrome::kMetadataScheme));
+ EXPECT_FALSE(IsExtensionScheme(chrome::kSwappedOutScheme));
+ EXPECT_FALSE(IsExtensionScheme(chrome::kViewSourceScheme));
+}
+
+TEST_F(BrowsingDataHelperTest, TestMatches) {
+ scoped_refptr<MockExtensionSpecialStoragePolicy> mock_policy =
+ new MockExtensionSpecialStoragePolicy;
+ // Protect kOrigin1.
+ mock_policy->AddProtected(kOrigin1.GetOrigin());
+
+ EXPECT_FALSE(Match(kOrigin1, kUnprotected, mock_policy));
+ EXPECT_TRUE(Match(kOrigin2, kUnprotected, mock_policy));
+ EXPECT_FALSE(Match(kOriginExt, kUnprotected, mock_policy));
+ EXPECT_FALSE(Match(kOriginDevTools, kUnprotected, mock_policy));
+
+ EXPECT_TRUE(Match(kOrigin1, kProtected, mock_policy));
+ EXPECT_FALSE(Match(kOrigin2, kProtected, mock_policy));
+ EXPECT_FALSE(Match(kOriginExt, kProtected, mock_policy));
+ EXPECT_FALSE(Match(kOriginDevTools, kProtected, mock_policy));
+
+ EXPECT_FALSE(Match(kOrigin1, kExtension, mock_policy));
+ EXPECT_FALSE(Match(kOrigin2, kExtension, mock_policy));
+ EXPECT_TRUE(Match(kOriginExt, kExtension, mock_policy));
+ EXPECT_FALSE(Match(kOriginDevTools, kExtension, mock_policy));
+
+ EXPECT_TRUE(Match(kOrigin1, kUnprotected | kProtected, mock_policy));
+ EXPECT_TRUE(Match(kOrigin2, kUnprotected | kProtected, mock_policy));
+ EXPECT_FALSE(Match(kOriginExt, kUnprotected | kProtected, mock_policy));
+ EXPECT_FALSE(Match(kOriginDevTools, kUnprotected | kProtected, mock_policy));
+
+ EXPECT_FALSE(Match(kOrigin1, kUnprotected | kExtension, mock_policy));
+ EXPECT_TRUE(Match(kOrigin2, kUnprotected | kExtension, mock_policy));
+ EXPECT_TRUE(Match(kOriginExt, kUnprotected | kExtension, mock_policy));
+ EXPECT_FALSE(Match(kOriginDevTools, kUnprotected | kExtension, mock_policy));
+
+ EXPECT_TRUE(Match(kOrigin1, kProtected | kExtension, mock_policy));
+ EXPECT_FALSE(Match(kOrigin2, kProtected | kExtension, mock_policy));
+ EXPECT_TRUE(Match(kOriginExt, kProtected | kExtension, mock_policy));
+ EXPECT_FALSE(Match(kOriginDevTools, kProtected | kExtension, mock_policy));
+
+ EXPECT_TRUE(Match(kOrigin1, kUnprotected | kProtected | kExtension,
+ mock_policy));
+ EXPECT_TRUE(Match(kOrigin2, kUnprotected | kProtected | kExtension,
+ mock_policy));
+ EXPECT_TRUE(Match(kOriginExt, kUnprotected | kProtected | kExtension,
+ mock_policy));
+ EXPECT_FALSE(Match(kOriginDevTools, kUnprotected | kProtected | kExtension,
+ mock_policy));
+}
+
+} // namespace
diff --git a/chrome/browser/browsing_data/browsing_data_indexed_db_helper.cc b/chrome/browser/browsing_data/browsing_data_indexed_db_helper.cc
new file mode 100644
index 0000000..823c4878
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_indexed_db_helper.cc
@@ -0,0 +1,263 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/file_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/indexed_db_context.h"
+#include "webkit/database/database_util.h"
+#include "webkit/glue/webkit_glue.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+using content::IndexedDBContext;
+using webkit_database::DatabaseUtil;
+
+namespace {
+
+class BrowsingDataIndexedDBHelperImpl : public BrowsingDataIndexedDBHelper {
+ public:
+ explicit BrowsingDataIndexedDBHelperImpl(Profile* profile);
+
+ virtual void StartFetching(
+ const base::Callback<void(const std::list<IndexedDBInfo>&)>&
+ callback) OVERRIDE;
+ virtual void DeleteIndexedDB(const GURL& origin) OVERRIDE;
+
+ private:
+ virtual ~BrowsingDataIndexedDBHelperImpl();
+
+ // Enumerates all indexed database files in the WEBKIT thread.
+ void FetchIndexedDBInfoInWebKitThread();
+ // Notifies the completion callback in the UI thread.
+ void NotifyInUIThread();
+ // Delete a single indexed database in the WEBKIT thread.
+ void DeleteIndexedDBInWebKitThread(const GURL& origin);
+
+ scoped_refptr<IndexedDBContext> indexed_db_context_;
+
+ // Access to |indexed_db_info_| is triggered indirectly via the UI thread and
+ // guarded by |is_fetching_|. This means |indexed_db_info_| is only accessed
+ // while |is_fetching_| is true. The flag |is_fetching_| is only accessed on
+ // the UI thread.
+ // In the context of this class |indexed_db_info_| is only accessed on the
+ // WEBKIT thread.
+ std::list<IndexedDBInfo> indexed_db_info_;
+
+ // This only mutates on the UI thread.
+ base::Callback<void(const std::list<IndexedDBInfo>&)> completion_callback_;
+
+ // Indicates whether or not we're currently fetching information:
+ // it's true when StartFetching() is called in the UI thread, and it's reset
+ // after we notified the callback in the UI thread.
+ // This only mutates on the UI thread.
+ bool is_fetching_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataIndexedDBHelperImpl);
+};
+
+BrowsingDataIndexedDBHelperImpl::BrowsingDataIndexedDBHelperImpl(
+ Profile* profile)
+ : indexed_db_context_(BrowserContext::GetIndexedDBContext(profile)),
+ is_fetching_(false) {
+ DCHECK(indexed_db_context_.get());
+}
+
+BrowsingDataIndexedDBHelperImpl::~BrowsingDataIndexedDBHelperImpl() {
+}
+
+void BrowsingDataIndexedDBHelperImpl::StartFetching(
+ const base::Callback<void(const std::list<IndexedDBInfo>&)>& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!is_fetching_);
+ DCHECK_EQ(false, callback.is_null());
+
+ is_fetching_ = true;
+ completion_callback_ = callback;
+ BrowserThread::PostTask(
+ BrowserThread::WEBKIT_DEPRECATED, FROM_HERE,
+ base::Bind(
+ &BrowsingDataIndexedDBHelperImpl::FetchIndexedDBInfoInWebKitThread,
+ this));
+}
+
+void BrowsingDataIndexedDBHelperImpl::DeleteIndexedDB(
+ const GURL& origin) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ BrowserThread::PostTask(
+ BrowserThread::WEBKIT_DEPRECATED, FROM_HERE,
+ base::Bind(
+ &BrowsingDataIndexedDBHelperImpl::DeleteIndexedDBInWebKitThread, this,
+ origin));
+}
+
+void BrowsingDataIndexedDBHelperImpl::FetchIndexedDBInfoInWebKitThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
+ std::vector<GURL> origins = indexed_db_context_->GetAllOrigins();
+ for (std::vector<GURL>::const_iterator iter = origins.begin();
+ iter != origins.end(); ++iter) {
+ const GURL& origin = *iter;
+ if (!BrowsingDataHelper::HasWebScheme(origin))
+ continue; // Non-websafe state is not considered browsing data.
+
+ indexed_db_info_.push_back(IndexedDBInfo(
+ origin,
+ indexed_db_context_->GetOriginDiskUsage(origin),
+ indexed_db_context_->GetOriginLastModified(origin)));
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&BrowsingDataIndexedDBHelperImpl::NotifyInUIThread, this));
+}
+
+void BrowsingDataIndexedDBHelperImpl::NotifyInUIThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(is_fetching_);
+ completion_callback_.Run(indexed_db_info_);
+ completion_callback_.Reset();
+ is_fetching_ = false;
+}
+
+void BrowsingDataIndexedDBHelperImpl::DeleteIndexedDBInWebKitThread(
+ const GURL& origin) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
+ indexed_db_context_->DeleteForOrigin(origin);
+}
+
+} // namespace
+
+BrowsingDataIndexedDBHelper::IndexedDBInfo::IndexedDBInfo(
+ const GURL& origin,
+ int64 size,
+ base::Time last_modified)
+ : origin(origin),
+ size(size),
+ last_modified(last_modified) {
+}
+
+BrowsingDataIndexedDBHelper::IndexedDBInfo::~IndexedDBInfo() {}
+
+// static
+BrowsingDataIndexedDBHelper* BrowsingDataIndexedDBHelper::Create(
+ Profile* profile) {
+ return new BrowsingDataIndexedDBHelperImpl(profile);
+}
+
+CannedBrowsingDataIndexedDBHelper::
+PendingIndexedDBInfo::PendingIndexedDBInfo(const GURL& origin,
+ const string16& name)
+ : origin(origin),
+ name(name) {
+}
+
+CannedBrowsingDataIndexedDBHelper::
+PendingIndexedDBInfo::~PendingIndexedDBInfo() {
+}
+
+bool CannedBrowsingDataIndexedDBHelper::PendingIndexedDBInfo::operator<(
+ const PendingIndexedDBInfo& other) const {
+ if (origin == other.origin)
+ return name < other.name;
+ return origin < other.origin;
+}
+
+CannedBrowsingDataIndexedDBHelper::CannedBrowsingDataIndexedDBHelper()
+ : is_fetching_(false) {
+}
+
+CannedBrowsingDataIndexedDBHelper::~CannedBrowsingDataIndexedDBHelper() {}
+
+CannedBrowsingDataIndexedDBHelper* CannedBrowsingDataIndexedDBHelper::Clone() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ CannedBrowsingDataIndexedDBHelper* clone =
+ new CannedBrowsingDataIndexedDBHelper();
+
+ base::AutoLock auto_lock(lock_);
+ clone->pending_indexed_db_info_ = pending_indexed_db_info_;
+ clone->indexed_db_info_ = indexed_db_info_;
+ return clone;
+}
+
+void CannedBrowsingDataIndexedDBHelper::AddIndexedDB(
+ const GURL& origin, const string16& name) {
+ if (!BrowsingDataHelper::HasWebScheme(origin))
+ return; // Non-websafe state is not considered browsing data.
+
+ base::AutoLock auto_lock(lock_);
+ pending_indexed_db_info_.insert(PendingIndexedDBInfo(origin, name));
+}
+
+void CannedBrowsingDataIndexedDBHelper::Reset() {
+ base::AutoLock auto_lock(lock_);
+ indexed_db_info_.clear();
+ pending_indexed_db_info_.clear();
+}
+
+bool CannedBrowsingDataIndexedDBHelper::empty() const {
+ base::AutoLock auto_lock(lock_);
+ return indexed_db_info_.empty() && pending_indexed_db_info_.empty();
+}
+
+size_t CannedBrowsingDataIndexedDBHelper::GetIndexedDBCount() const {
+ base::AutoLock auto_lock(lock_);
+ return pending_indexed_db_info_.size();
+}
+
+const std::set<CannedBrowsingDataIndexedDBHelper::PendingIndexedDBInfo>&
+CannedBrowsingDataIndexedDBHelper::GetIndexedDBInfo() const {
+ base::AutoLock auto_lock(lock_);
+ return pending_indexed_db_info_;
+}
+
+void CannedBrowsingDataIndexedDBHelper::StartFetching(
+ const base::Callback<void(const std::list<IndexedDBInfo>&)>& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!is_fetching_);
+ DCHECK_EQ(false, callback.is_null());
+
+ is_fetching_ = true;
+ completion_callback_ = callback;
+ BrowserThread::PostTask(
+ BrowserThread::WEBKIT_DEPRECATED, FROM_HERE,
+ base::Bind(
+ &CannedBrowsingDataIndexedDBHelper::ConvertPendingInfoInWebKitThread,
+ this));
+}
+
+void CannedBrowsingDataIndexedDBHelper::ConvertPendingInfoInWebKitThread() {
+ base::AutoLock auto_lock(lock_);
+ indexed_db_info_.clear();
+ for (std::set<PendingIndexedDBInfo>::const_iterator
+ info = pending_indexed_db_info_.begin();
+ info != pending_indexed_db_info_.end(); ++info) {
+ indexed_db_info_.push_back(IndexedDBInfo(
+ info->origin,
+ 0,
+ base::Time()));
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&CannedBrowsingDataIndexedDBHelper::NotifyInUIThread, this));
+}
+
+void CannedBrowsingDataIndexedDBHelper::NotifyInUIThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(is_fetching_);
+
+ completion_callback_.Run(indexed_db_info_);
+ completion_callback_.Reset();
+ is_fetching_ = false;
+}
diff --git a/chrome/browser/browsing_data/browsing_data_indexed_db_helper.h b/chrome/browser/browsing_data/browsing_data_indexed_db_helper.h
new file mode 100644
index 0000000..de0504c
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_indexed_db_helper.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_INDEXED_DB_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_INDEXED_DB_HELPER_H_
+
+#include <list>
+#include <set>
+#include <string>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/time.h"
+#include "googleurl/src/gurl.h"
+
+class Profile;
+
+// BrowsingDataIndexedDBHelper is an interface for classes dealing with
+// aggregating and deleting browsing data stored in indexed databases. A
+// client of this class need to call StartFetching from the UI thread to
+// initiate the flow, and it'll be notified by the callback in its UI thread at
+// some later point.
+class BrowsingDataIndexedDBHelper
+ : public base::RefCountedThreadSafe<BrowsingDataIndexedDBHelper> {
+ public:
+ // Contains detailed information about an indexed database.
+ struct IndexedDBInfo {
+ IndexedDBInfo(
+ const GURL& origin,
+ int64 size,
+ base::Time last_modified);
+ ~IndexedDBInfo();
+
+ GURL origin;
+ int64 size;
+ base::Time last_modified;
+ };
+
+ // Create a BrowsingDataIndexedDBHelper instance for the indexed databases
+ // stored in |profile|'s user data directory.
+ static BrowsingDataIndexedDBHelper* Create(Profile* profile);
+
+ // Starts the fetching process, which will notify its completion via
+ // callback.
+ // This must be called only in the UI thread.
+ virtual void StartFetching(
+ const base::Callback<void(const std::list<IndexedDBInfo>&)>&
+ callback) = 0;
+ // Requests a single indexed database to be deleted in the WEBKIT thread.
+ virtual void DeleteIndexedDB(const GURL& origin) = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<BrowsingDataIndexedDBHelper>;
+ virtual ~BrowsingDataIndexedDBHelper() {}
+};
+
+// This class is an implementation of BrowsingDataIndexedDBHelper that does
+// not fetch its information from the indexed database tracker, but gets them
+// passed as a parameter.
+class CannedBrowsingDataIndexedDBHelper
+ : public BrowsingDataIndexedDBHelper {
+ public:
+ // Contains information about an indexed database.
+ struct PendingIndexedDBInfo {
+ PendingIndexedDBInfo(const GURL& origin, const string16& name);
+ ~PendingIndexedDBInfo();
+
+ bool operator<(const PendingIndexedDBInfo& other) const;
+
+ GURL origin;
+ string16 name;
+ };
+
+ CannedBrowsingDataIndexedDBHelper();
+
+ // Return a copy of the IndexedDB helper. Only one consumer can use the
+ // StartFetching method at a time, so we need to create a copy of the helper
+ // every time we instantiate a cookies tree model for it.
+ CannedBrowsingDataIndexedDBHelper* Clone();
+
+ // Add a indexed database to the set of canned indexed databases that is
+ // returned by this helper.
+ void AddIndexedDB(const GURL& origin,
+ const string16& name);
+
+ // Clear the list of canned indexed databases.
+ void Reset();
+
+ // True if no indexed databases are currently stored.
+ bool empty() const;
+
+ // Returns the number of currently stored indexed databases.
+ size_t GetIndexedDBCount() const;
+
+ // Returns the current list of indexed data bases.
+ const std::set<CannedBrowsingDataIndexedDBHelper::PendingIndexedDBInfo>&
+ GetIndexedDBInfo() const;
+
+ // BrowsingDataIndexedDBHelper methods.
+ virtual void StartFetching(
+ const base::Callback<void(const std::list<IndexedDBInfo>&)>&
+ callback) OVERRIDE;
+
+ virtual void DeleteIndexedDB(const GURL& origin) OVERRIDE {}
+
+ private:
+ virtual ~CannedBrowsingDataIndexedDBHelper();
+
+ // Convert the pending indexed db info to indexed db info objects.
+ void ConvertPendingInfoInWebKitThread();
+
+ void NotifyInUIThread();
+
+ // Lock to protect access to pending_indexed_db_info_;
+ mutable base::Lock lock_;
+
+ // Access to |pending_indexed_db_info_| is protected by |lock_| since it can
+ // be accessed on the UI and on the WEBKIT thread.
+ std::set<PendingIndexedDBInfo> pending_indexed_db_info_;
+
+ // Access to |indexed_db_info_| is triggered indirectly via the UI thread and
+ // guarded by |is_fetching_|. This means |indexed_db_info_| is only accessed
+ // while |is_fetching_| is true. The flag |is_fetching_| is only accessed on
+ // the UI thread.
+ // In the context of this class |indexed_db_info_| is only accessed on the UI
+ // thread.
+ std::list<IndexedDBInfo> indexed_db_info_;
+
+ // This only mutates on the UI thread.
+ base::Callback<void(const std::list<IndexedDBInfo>&)> completion_callback_;
+
+ // Indicates whether or not we're currently fetching information:
+ // it's true when StartFetching() is called in the UI thread, and it's reset
+ // after we notified the callback in the UI thread.
+ // This only mutates on the UI thread.
+ bool is_fetching_;
+
+ DISALLOW_COPY_AND_ASSIGN(CannedBrowsingDataIndexedDBHelper);
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_INDEXED_DB_HELPER_H_
diff --git a/chrome/browser/browsing_data/browsing_data_indexed_db_helper_browsertest.cc b/chrome/browser/browsing_data/browsing_data_indexed_db_helper_browsertest.cc
new file mode 100644
index 0000000..da2288f
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_indexed_db_helper_browsertest.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 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 <string>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browsing_data/browsing_data_helper_browsertest.h"
+#include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+typedef BrowsingDataHelperCallback<BrowsingDataIndexedDBHelper::IndexedDBInfo>
+ TestCompletionCallback;
+
+typedef InProcessBrowserTest BrowsingDataIndexedDBHelperTest;
+
+IN_PROC_BROWSER_TEST_F(BrowsingDataIndexedDBHelperTest, CannedAddIndexedDB) {
+ const GURL origin1("http://host1:1/");
+ const GURL origin2("http://host2:1/");
+ const string16 description(ASCIIToUTF16("description"));
+
+ scoped_refptr<CannedBrowsingDataIndexedDBHelper> helper(
+ new CannedBrowsingDataIndexedDBHelper());
+ helper->AddIndexedDB(origin1, description);
+ helper->AddIndexedDB(origin2, description);
+
+ TestCompletionCallback callback;
+ helper->StartFetching(
+ base::Bind(&TestCompletionCallback::callback,
+ base::Unretained(&callback)));
+
+ std::list<BrowsingDataIndexedDBHelper::IndexedDBInfo> result =
+ callback.result();
+
+ ASSERT_EQ(2U, result.size());
+ std::list<BrowsingDataIndexedDBHelper::IndexedDBInfo>::iterator info =
+ result.begin();
+ EXPECT_EQ(origin1, info->origin);
+ info++;
+ EXPECT_EQ(origin2, info->origin);
+}
+
+IN_PROC_BROWSER_TEST_F(BrowsingDataIndexedDBHelperTest, CannedUnique) {
+ const GURL origin("http://host1:1/");
+ const string16 description(ASCIIToUTF16("description"));
+
+ scoped_refptr<CannedBrowsingDataIndexedDBHelper> helper(
+ new CannedBrowsingDataIndexedDBHelper());
+ helper->AddIndexedDB(origin, description);
+ helper->AddIndexedDB(origin, description);
+
+ TestCompletionCallback callback;
+ helper->StartFetching(
+ base::Bind(&TestCompletionCallback::callback,
+ base::Unretained(&callback)));
+
+ std::list<BrowsingDataIndexedDBHelper::IndexedDBInfo> result =
+ callback.result();
+
+ ASSERT_EQ(1U, result.size());
+ EXPECT_EQ(origin, result.begin()->origin);
+}
+} // namespace
diff --git a/chrome/browser/browsing_data/browsing_data_indexed_db_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_indexed_db_helper_unittest.cc
new file mode 100644
index 0000000..b2f5118
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_indexed_db_helper_unittest.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 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 "testing/gtest/include/gtest/gtest.h"
+
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
+#include "chrome/test/base/testing_profile.h"
+
+namespace {
+
+typedef testing::Test CannedBrowsingDataIndexedDBHelperTest;
+
+TEST_F(CannedBrowsingDataIndexedDBHelperTest, Empty) {
+ const GURL origin("http://host1:1/");
+ const string16 description(ASCIIToUTF16("description"));
+
+ scoped_refptr<CannedBrowsingDataIndexedDBHelper> helper(
+ new CannedBrowsingDataIndexedDBHelper());
+
+ ASSERT_TRUE(helper->empty());
+ helper->AddIndexedDB(origin, description);
+ ASSERT_FALSE(helper->empty());
+ helper->Reset();
+ ASSERT_TRUE(helper->empty());
+}
+
+TEST_F(CannedBrowsingDataIndexedDBHelperTest, IgnoreExtensionsAndDevTools) {
+ const GURL origin1("chrome-extension://abcdefghijklmnopqrstuvwxyz/");
+ const GURL origin2("chrome-devtools://abcdefghijklmnopqrstuvwxyz/");
+ const string16 description(ASCIIToUTF16("description"));
+
+ scoped_refptr<CannedBrowsingDataIndexedDBHelper> helper(
+ new CannedBrowsingDataIndexedDBHelper());
+
+ ASSERT_TRUE(helper->empty());
+ helper->AddIndexedDB(origin1, description);
+ ASSERT_TRUE(helper->empty());
+ helper->AddIndexedDB(origin2, description);
+ ASSERT_TRUE(helper->empty());
+}
+
+} // namespace
diff --git a/chrome/browser/browsing_data/browsing_data_local_storage_helper.cc b/chrome/browser/browsing_data/browsing_data_local_storage_helper.cc
new file mode 100644
index 0000000..820f63f
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_local_storage_helper.cc
@@ -0,0 +1,150 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/dom_storage_context.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+using content::DOMStorageContext;
+using dom_storage::DomStorageContext;
+
+BrowsingDataLocalStorageHelper::LocalStorageInfo::LocalStorageInfo(
+ const GURL& origin_url, int64 size, base::Time last_modified)
+ : origin_url(origin_url), size(size), last_modified(last_modified) {}
+
+BrowsingDataLocalStorageHelper::LocalStorageInfo::~LocalStorageInfo() {}
+
+BrowsingDataLocalStorageHelper::BrowsingDataLocalStorageHelper(
+ Profile* profile)
+ : dom_storage_context_(
+ BrowserContext::GetDefaultDOMStorageContext(profile)),
+ is_fetching_(false) {
+ DCHECK(dom_storage_context_);
+}
+
+BrowsingDataLocalStorageHelper::~BrowsingDataLocalStorageHelper() {
+}
+
+void BrowsingDataLocalStorageHelper::StartFetching(
+ const base::Callback<void(const std::list<LocalStorageInfo>&)>& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!is_fetching_);
+ DCHECK_EQ(false, callback.is_null());
+
+ is_fetching_ = true;
+ completion_callback_ = callback;
+ dom_storage_context_->GetUsageInfo(
+ base::Bind(
+ &BrowsingDataLocalStorageHelper::GetUsageInfoCallback, this));
+}
+
+void BrowsingDataLocalStorageHelper::DeleteOrigin(const GURL& origin) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ dom_storage_context_->DeleteOrigin(origin);
+}
+
+void BrowsingDataLocalStorageHelper::GetUsageInfoCallback(
+ const std::vector<DomStorageContext::UsageInfo>& infos) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ for (size_t i = 0; i < infos.size(); ++i) {
+ // Non-websafe state is not considered browsing data.
+ const DomStorageContext::UsageInfo& info = infos[i];
+ if (BrowsingDataHelper::HasWebScheme(info.origin)) {
+ local_storage_info_.push_back(
+ LocalStorageInfo(info.origin, info.data_size, info.last_modified));
+ }
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&BrowsingDataLocalStorageHelper::CallCompletionCallback,
+ this));
+}
+
+void BrowsingDataLocalStorageHelper::CallCompletionCallback() {
+ DCHECK(is_fetching_);
+ completion_callback_.Run(local_storage_info_);
+ completion_callback_.Reset();
+ is_fetching_ = false;
+}
+
+//---------------------------------------------------------
+
+CannedBrowsingDataLocalStorageHelper::CannedBrowsingDataLocalStorageHelper(
+ Profile* profile)
+ : BrowsingDataLocalStorageHelper(profile),
+ profile_(profile) {
+}
+
+CannedBrowsingDataLocalStorageHelper*
+CannedBrowsingDataLocalStorageHelper::Clone() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ CannedBrowsingDataLocalStorageHelper* clone =
+ new CannedBrowsingDataLocalStorageHelper(profile_);
+
+ clone->pending_local_storage_info_ = pending_local_storage_info_;
+ return clone;
+}
+
+void CannedBrowsingDataLocalStorageHelper::AddLocalStorage(
+ const GURL& origin) {
+ if (BrowsingDataHelper::HasWebScheme(origin))
+ pending_local_storage_info_.insert(origin);
+}
+
+void CannedBrowsingDataLocalStorageHelper::Reset() {
+ pending_local_storage_info_.clear();
+}
+
+bool CannedBrowsingDataLocalStorageHelper::empty() const {
+ return pending_local_storage_info_.empty();
+}
+
+size_t CannedBrowsingDataLocalStorageHelper::GetLocalStorageCount() const {
+ return pending_local_storage_info_.size();
+}
+
+const std::set<GURL>&
+CannedBrowsingDataLocalStorageHelper::GetLocalStorageInfo() const {
+ return pending_local_storage_info_;
+}
+
+void CannedBrowsingDataLocalStorageHelper::StartFetching(
+ const base::Callback<void(const std::list<LocalStorageInfo>&)>& callback) {
+ DCHECK(!is_fetching_);
+ DCHECK_EQ(false, callback.is_null());
+
+ is_fetching_ = true;
+ completion_callback_ = callback;
+
+ // We post a task to emulate async fetching behavior.
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&CannedBrowsingDataLocalStorageHelper::
+ ConvertPendingInfo, this));
+}
+
+CannedBrowsingDataLocalStorageHelper::~CannedBrowsingDataLocalStorageHelper() {}
+
+void CannedBrowsingDataLocalStorageHelper::ConvertPendingInfo() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ local_storage_info_.clear();
+ for (std::set<GURL>::iterator iter = pending_local_storage_info_.begin();
+ iter != pending_local_storage_info_.end(); ++iter) {
+ local_storage_info_.push_back(
+ LocalStorageInfo(*iter, 0, base::Time()));
+ }
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&CannedBrowsingDataLocalStorageHelper::CallCompletionCallback,
+ this));
+}
diff --git a/chrome/browser/browsing_data/browsing_data_local_storage_helper.h b/chrome/browser/browsing_data/browsing_data_local_storage_helper.h
new file mode 100644
index 0000000..4acd3cb
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_local_storage_helper.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_LOCAL_STORAGE_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_LOCAL_STORAGE_HELPER_H_
+
+#include <list>
+#include <set>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/time.h"
+#include "content/public/browser/dom_storage_context.h"
+#include "chrome/common/url_constants.h"
+#include "googleurl/src/gurl.h"
+
+class Profile;
+
+// This class fetches local storage information and provides a
+// means to delete the data associated with an origin.
+class BrowsingDataLocalStorageHelper
+ : public base::RefCounted<BrowsingDataLocalStorageHelper> {
+ public:
+ // Contains detailed information about local storage.
+ struct LocalStorageInfo {
+ LocalStorageInfo(
+ const GURL& origin_url,
+ int64 size,
+ base::Time last_modified);
+ ~LocalStorageInfo();
+
+ GURL origin_url;
+ int64 size;
+ base::Time last_modified;
+ };
+
+ explicit BrowsingDataLocalStorageHelper(Profile* profile);
+
+ // Starts the fetching process, which will notify its completion via
+ // callback. This must be called only in the UI thread.
+ virtual void StartFetching(
+ const base::Callback<void(const std::list<LocalStorageInfo>&)>& callback);
+
+ // Deletes the local storage for the |origin|.
+ virtual void DeleteOrigin(const GURL& origin);
+
+ protected:
+ friend class base::RefCounted<BrowsingDataLocalStorageHelper>;
+ virtual ~BrowsingDataLocalStorageHelper();
+
+ void CallCompletionCallback();
+
+ content::DOMStorageContext* dom_storage_context_; // Owned by the profile
+ base::Callback<void(const std::list<LocalStorageInfo>&)> completion_callback_;
+ bool is_fetching_;
+ std::list<LocalStorageInfo> local_storage_info_;
+
+ private:
+ void GetUsageInfoCallback(
+ const std::vector<dom_storage::DomStorageContext::UsageInfo>& infos);
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataLocalStorageHelper);
+};
+
+// This class is a thin wrapper around BrowsingDataLocalStorageHelper that does
+// not fetch its information from the local storage tracker, but gets them
+// passed as a parameter during construction.
+class CannedBrowsingDataLocalStorageHelper
+ : public BrowsingDataLocalStorageHelper {
+ public:
+ explicit CannedBrowsingDataLocalStorageHelper(Profile* profile);
+
+ // Return a copy of the local storage helper. Only one consumer can use the
+ // StartFetching method at a time, so we need to create a copy of the helper
+ // every time we instantiate a cookies tree model for it.
+ CannedBrowsingDataLocalStorageHelper* Clone();
+
+ // Add a local storage to the set of canned local storages that is returned
+ // by this helper.
+ void AddLocalStorage(const GURL& origin);
+
+ // Clear the list of canned local storages.
+ void Reset();
+
+ // True if no local storages are currently stored.
+ bool empty() const;
+
+ // Returns the number of local storages currently stored.
+ size_t GetLocalStorageCount() const;
+
+ // Returns the set of origins that use local storage.
+ const std::set<GURL>& GetLocalStorageInfo() const;
+
+ // BrowsingDataLocalStorageHelper implementation.
+ virtual void StartFetching(
+ const base::Callback<void(const std::list<LocalStorageInfo>&)>& callback)
+ OVERRIDE;
+
+ private:
+ virtual ~CannedBrowsingDataLocalStorageHelper();
+
+ // Convert the pending local storage info to local storage info objects.
+ void ConvertPendingInfo();
+
+ std::set<GURL> pending_local_storage_info_;
+
+ Profile* profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(CannedBrowsingDataLocalStorageHelper);
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_LOCAL_STORAGE_HELPER_H_
diff --git a/chrome/browser/browsing_data/browsing_data_local_storage_helper_browsertest.cc b/chrome/browser/browsing_data/browsing_data_local_storage_helper_browsertest.cc
new file mode 100644
index 0000000..8fb9a10
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_local_storage_helper_browsertest.cc
@@ -0,0 +1,191 @@
+// Copyright (c) 2012 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 <string>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/test/thread_test_helper.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browsing_data/browsing_data_helper_browsertest.h"
+#include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/dom_storage_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+using content::DOMStorageContext;
+
+namespace {
+typedef
+ BrowsingDataHelperCallback<BrowsingDataLocalStorageHelper::LocalStorageInfo>
+ TestCompletionCallback;
+
+const FilePath::CharType kTestFile0[] =
+ FILE_PATH_LITERAL("http_www.chromium.org_0.localstorage");
+
+const char kOriginOfTestFile0[] = "http://www.chromium.org/";
+
+const FilePath::CharType kTestFile1[] =
+ FILE_PATH_LITERAL("http_www.google.com_0.localstorage");
+
+const FilePath::CharType kTestFileInvalid[] =
+ FILE_PATH_LITERAL("http_www.google.com_localstorage_0.foo");
+
+// This is only here to test that extension state is not listed by the helper.
+const FilePath::CharType kTestFileExtension[] = FILE_PATH_LITERAL(
+ "chrome-extension_behllobkkfkfnphdnhnkndlbkcpglgmj_0.localstorage");
+
+class BrowsingDataLocalStorageHelperTest : public InProcessBrowserTest {
+ protected:
+ void CreateLocalStorageFilesForTest() {
+ // Note: This helper depends on details of how the dom_storage library
+ // stores data in the host file system.
+ FilePath storage_path = GetLocalStoragePathForTestingProfile();
+ file_util::CreateDirectory(storage_path);
+ const FilePath::CharType* kFilesToCreate[] = {
+ kTestFile0, kTestFile1, kTestFileInvalid, kTestFileExtension
+ };
+ for (size_t i = 0; i < arraysize(kFilesToCreate); ++i) {
+ FilePath file_path = storage_path.Append(kFilesToCreate[i]);
+ file_util::WriteFile(file_path, NULL, 0);
+ }
+ }
+
+ FilePath GetLocalStoragePathForTestingProfile() {
+ return browser()->profile()->GetPath().AppendASCII("Local Storage");
+ }
+};
+
+// This class is notified by BrowsingDataLocalStorageHelper on the UI thread
+// once it finishes fetching the local storage data.
+class StopTestOnCallback {
+ public:
+ explicit StopTestOnCallback(
+ BrowsingDataLocalStorageHelper* local_storage_helper)
+ : local_storage_helper_(local_storage_helper) {
+ DCHECK(local_storage_helper_);
+ }
+
+ void Callback(
+ const std::list<BrowsingDataLocalStorageHelper::LocalStorageInfo>&
+ local_storage_info) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // There's no guarantee on the order, ensure these files are there.
+ const char* const kTestHosts[] = {"www.chromium.org", "www.google.com"};
+ bool test_hosts_found[arraysize(kTestHosts)] = {false, false};
+ ASSERT_EQ(arraysize(kTestHosts), local_storage_info.size());
+ typedef std::list<BrowsingDataLocalStorageHelper::LocalStorageInfo>
+ LocalStorageInfoList;
+ for (size_t i = 0; i < arraysize(kTestHosts); ++i) {
+ for (LocalStorageInfoList::const_iterator info =
+ local_storage_info.begin(); info != local_storage_info.end();
+ ++info) {
+ ASSERT_TRUE(info->origin_url.SchemeIs("http"));
+ if (info->origin_url.host() == kTestHosts[i]) {
+ ASSERT_FALSE(test_hosts_found[i]);
+ test_hosts_found[i] = true;
+ }
+ }
+ }
+ for (size_t i = 0; i < arraysize(kTestHosts); ++i) {
+ ASSERT_TRUE(test_hosts_found[i]) << kTestHosts[i];
+ }
+ MessageLoop::current()->Quit();
+ }
+
+ private:
+ BrowsingDataLocalStorageHelper* local_storage_helper_;
+};
+
+IN_PROC_BROWSER_TEST_F(BrowsingDataLocalStorageHelperTest, CallbackCompletes) {
+ scoped_refptr<BrowsingDataLocalStorageHelper> local_storage_helper(
+ new BrowsingDataLocalStorageHelper(browser()->profile()));
+ CreateLocalStorageFilesForTest();
+ StopTestOnCallback stop_test_on_callback(local_storage_helper);
+ local_storage_helper->StartFetching(
+ base::Bind(&StopTestOnCallback::Callback,
+ base::Unretained(&stop_test_on_callback)));
+ // Blocks until StopTestOnCallback::Callback is notified.
+ ui_test_utils::RunMessageLoop();
+}
+
+IN_PROC_BROWSER_TEST_F(BrowsingDataLocalStorageHelperTest, DeleteSingleFile) {
+ scoped_refptr<BrowsingDataLocalStorageHelper> local_storage_helper(
+ new BrowsingDataLocalStorageHelper(browser()->profile()));
+ CreateLocalStorageFilesForTest();
+ local_storage_helper->DeleteOrigin(GURL(kOriginOfTestFile0));
+ BrowserThread::GetBlockingPool()->FlushForTesting();
+
+ // Ensure the file has been deleted.
+ file_util::FileEnumerator file_enumerator(
+ GetLocalStoragePathForTestingProfile(),
+ false,
+ file_util::FileEnumerator::FILES);
+ int num_files = 0;
+ for (FilePath file_path = file_enumerator.Next();
+ !file_path.empty();
+ file_path = file_enumerator.Next()) {
+ ASSERT_FALSE(FilePath(kTestFile0) == file_path.BaseName());
+ ++num_files;
+ }
+ ASSERT_EQ(3, num_files);
+}
+
+IN_PROC_BROWSER_TEST_F(BrowsingDataLocalStorageHelperTest,
+ CannedAddLocalStorage) {
+ const GURL origin1("http://host1:1/");
+ const GURL origin2("http://host2:1/");
+
+ scoped_refptr<CannedBrowsingDataLocalStorageHelper> helper(
+ new CannedBrowsingDataLocalStorageHelper(browser()->profile()));
+ helper->AddLocalStorage(origin1);
+ helper->AddLocalStorage(origin2);
+
+ TestCompletionCallback callback;
+ helper->StartFetching(
+ base::Bind(&TestCompletionCallback::callback,
+ base::Unretained(&callback)));
+
+ std::list<BrowsingDataLocalStorageHelper::LocalStorageInfo> result =
+ callback.result();
+
+ ASSERT_EQ(2u, result.size());
+ std::list<BrowsingDataLocalStorageHelper::LocalStorageInfo>::iterator info =
+ result.begin();
+ EXPECT_EQ(origin1, info->origin_url);
+ info++;
+ EXPECT_EQ(origin2, info->origin_url);
+}
+
+IN_PROC_BROWSER_TEST_F(BrowsingDataLocalStorageHelperTest, CannedUnique) {
+ const GURL origin("http://host1:1/");
+
+ scoped_refptr<CannedBrowsingDataLocalStorageHelper> helper(
+ new CannedBrowsingDataLocalStorageHelper(browser()->profile()));
+ helper->AddLocalStorage(origin);
+ helper->AddLocalStorage(origin);
+
+ TestCompletionCallback callback;
+ helper->StartFetching(
+ base::Bind(&TestCompletionCallback::callback,
+ base::Unretained(&callback)));
+
+ std::list<BrowsingDataLocalStorageHelper::LocalStorageInfo> result =
+ callback.result();
+
+ ASSERT_EQ(1u, result.size());
+ EXPECT_EQ(origin, result.begin()->origin_url);
+}
+} // namespace
diff --git a/chrome/browser/browsing_data/browsing_data_local_storage_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_local_storage_helper_unittest.cc
new file mode 100644
index 0000000..2ad644a
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_local_storage_helper_unittest.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
+
+#include "chrome/test/base/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+typedef testing::Test CannedBrowsingDataLocalStorageTest;
+
+TEST_F(CannedBrowsingDataLocalStorageTest, Empty) {
+ TestingProfile profile;
+
+ const GURL origin("http://host1:1/");
+
+ scoped_refptr<CannedBrowsingDataLocalStorageHelper> helper(
+ new CannedBrowsingDataLocalStorageHelper(&profile));
+
+ ASSERT_TRUE(helper->empty());
+ helper->AddLocalStorage(origin);
+ ASSERT_FALSE(helper->empty());
+ helper->Reset();
+ ASSERT_TRUE(helper->empty());
+}
+
+TEST_F(CannedBrowsingDataLocalStorageTest, IgnoreExtensionsAndDevTools) {
+ TestingProfile profile;
+
+ const GURL origin1("chrome-extension://abcdefghijklmnopqrstuvwxyz/");
+ const GURL origin2("chrome-devtools://abcdefghijklmnopqrstuvwxyz/");
+
+ scoped_refptr<CannedBrowsingDataLocalStorageHelper> helper(
+ new CannedBrowsingDataLocalStorageHelper(&profile));
+
+ ASSERT_TRUE(helper->empty());
+ helper->AddLocalStorage(origin1);
+ ASSERT_TRUE(helper->empty());
+ helper->AddLocalStorage(origin2);
+ ASSERT_TRUE(helper->empty());
+}
+
+} // namespace
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper.cc b/chrome/browser/browsing_data/browsing_data_quota_helper.cc
new file mode 100644
index 0000000..2b06311
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper.cc
@@ -0,0 +1,53 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_quota_helper.h"
+
+BrowsingDataQuotaHelper::QuotaInfo::QuotaInfo()
+ : temporary_usage(0),
+ persistent_usage(0) {}
+
+BrowsingDataQuotaHelper::QuotaInfo::QuotaInfo(const std::string& host)
+ : host(host),
+ temporary_usage(0),
+ persistent_usage(0) {}
+
+BrowsingDataQuotaHelper::QuotaInfo::QuotaInfo(const std::string& host,
+ int64 temporary_usage,
+ int64 persistent_usage)
+ : host(host),
+ temporary_usage(temporary_usage),
+ persistent_usage(persistent_usage) {}
+
+BrowsingDataQuotaHelper::QuotaInfo::~QuotaInfo() {}
+
+// static
+void BrowsingDataQuotaHelperDeleter::Destruct(
+ const BrowsingDataQuotaHelper* helper) {
+ helper->io_thread_->DeleteSoon(FROM_HERE, helper);
+}
+
+BrowsingDataQuotaHelper::BrowsingDataQuotaHelper(
+ base::MessageLoopProxy* io_thread)
+ : io_thread_(io_thread) {
+}
+
+BrowsingDataQuotaHelper::~BrowsingDataQuotaHelper() {
+}
+
+bool BrowsingDataQuotaHelper::QuotaInfo::operator <(
+ const BrowsingDataQuotaHelper::QuotaInfo& rhs) const {
+ if (this->host != rhs.host)
+ return this->host < rhs.host;
+ if (this->temporary_usage != rhs.temporary_usage)
+ return this->temporary_usage < rhs.temporary_usage;
+ return this->persistent_usage < rhs.persistent_usage;
+}
+
+bool BrowsingDataQuotaHelper::QuotaInfo::operator ==(
+ const BrowsingDataQuotaHelper::QuotaInfo& rhs) const {
+ return this->host == rhs.host &&
+ this->temporary_usage == rhs.temporary_usage &&
+ this->persistent_usage == rhs.persistent_usage;
+}
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper.h b/chrome/browser/browsing_data/browsing_data_quota_helper.h
new file mode 100644
index 0000000..192379e
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_QUOTA_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_QUOTA_HELPER_H_
+
+#include <list>
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop_proxy.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "base/time.h"
+#include "content/public/browser/browser_thread.h"
+#include "webkit/quota/quota_types.h"
+
+class BrowsingDataQuotaHelper;
+class Profile;
+
+struct BrowsingDataQuotaHelperDeleter {
+ static void Destruct(const BrowsingDataQuotaHelper* helper);
+};
+
+// This class is an interface class to bridge between Cookies Tree and Unified
+// Quota System. This class provides a way to get usage and quota information
+// through the instance.
+//
+// Call Create to create an instance for a profile and call StartFetching with
+// a callback to fetch information asynchronously.
+//
+// Parallel fetching is not allowed, a fetching task should start after end of
+// previous task. All method of this class should called from UI thread.
+class BrowsingDataQuotaHelper
+ : public base::RefCountedThreadSafe<BrowsingDataQuotaHelper,
+ BrowsingDataQuotaHelperDeleter> {
+ public:
+ // QuotaInfo contains host-based quota and usage information for persistent
+ // and temporary storage.
+ struct QuotaInfo {
+ QuotaInfo();
+ explicit QuotaInfo(const std::string& host);
+ QuotaInfo(const std::string& host,
+ int64 temporary_usage,
+ int64 persistent_usage);
+ ~QuotaInfo();
+
+ // Certain versions of MSVC 2008 have bad implementations of ADL for nested
+ // classes so they require these operators to be declared here instead of in
+ // the global namespace.
+ bool operator <(const QuotaInfo& rhs) const;
+ bool operator ==(const QuotaInfo& rhs) const;
+
+ std::string host;
+ int64 temporary_usage;
+ int64 persistent_usage;
+ };
+
+ typedef std::list<QuotaInfo> QuotaInfoArray;
+ typedef base::Callback<void(const QuotaInfoArray&)> FetchResultCallback;
+
+ static BrowsingDataQuotaHelper* Create(Profile* profile);
+
+ virtual void StartFetching(const FetchResultCallback& callback) = 0;
+
+ virtual void RevokeHostQuota(const std::string& host) = 0;
+
+ protected:
+ explicit BrowsingDataQuotaHelper(base::MessageLoopProxy* io_thread_);
+ virtual ~BrowsingDataQuotaHelper();
+
+ private:
+ friend class base::DeleteHelper<BrowsingDataQuotaHelper>;
+ friend struct BrowsingDataQuotaHelperDeleter;
+ scoped_refptr<base::MessageLoopProxy> io_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataQuotaHelper);
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_QUOTA_HELPER_H_
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc b/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc
new file mode 100644
index 0000000..b00268d5
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper_impl.cc
@@ -0,0 +1,178 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_quota_helper_impl.h"
+
+#include <map>
+#include <set>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "webkit/quota/quota_manager.h"
+
+using content::BrowserThread;
+
+// static
+BrowsingDataQuotaHelper* BrowsingDataQuotaHelper::Create(Profile* profile) {
+ return new BrowsingDataQuotaHelperImpl(
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
+ content::BrowserContext::GetQuotaManager(profile));
+}
+
+void BrowsingDataQuotaHelperImpl::StartFetching(
+ const FetchResultCallback& callback) {
+ DCHECK_EQ(false, callback.is_null());
+ DCHECK(callback_.is_null());
+ DCHECK(!is_fetching_);
+ callback_ = callback;
+ quota_info_.clear();
+ is_fetching_ = true;
+
+ FetchQuotaInfo();
+}
+
+void BrowsingDataQuotaHelperImpl::RevokeHostQuota(const std::string& host) {
+ if (!io_thread_->BelongsToCurrentThread()) {
+ io_thread_->PostTask(
+ FROM_HERE,
+ base::Bind(&BrowsingDataQuotaHelperImpl::RevokeHostQuota, this, host));
+ return;
+ }
+
+ quota_manager_->SetPersistentHostQuota(
+ host, 0,
+ base::Bind(&BrowsingDataQuotaHelperImpl::DidRevokeHostQuota,
+ weak_factory_.GetWeakPtr()));
+}
+
+BrowsingDataQuotaHelperImpl::BrowsingDataQuotaHelperImpl(
+ base::MessageLoopProxy* ui_thread,
+ base::MessageLoopProxy* io_thread,
+ quota::QuotaManager* quota_manager)
+ : BrowsingDataQuotaHelper(io_thread),
+ quota_manager_(quota_manager),
+ is_fetching_(false),
+ ui_thread_(ui_thread),
+ io_thread_(io_thread),
+ weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+ DCHECK(quota_manager);
+}
+
+BrowsingDataQuotaHelperImpl::~BrowsingDataQuotaHelperImpl() {}
+
+void BrowsingDataQuotaHelperImpl::FetchQuotaInfo() {
+ if (!io_thread_->BelongsToCurrentThread()) {
+ io_thread_->PostTask(
+ FROM_HERE,
+ base::Bind(&BrowsingDataQuotaHelperImpl::FetchQuotaInfo, this));
+ return;
+ }
+
+ quota_manager_->GetOriginsModifiedSince(
+ quota::kStorageTypeTemporary,
+ base::Time(),
+ base::Bind(&BrowsingDataQuotaHelperImpl::GotOrigins,
+ weak_factory_.GetWeakPtr()));
+}
+
+void BrowsingDataQuotaHelperImpl::GotOrigins(
+ const std::set<GURL>& origins, quota::StorageType type) {
+ for (std::set<GURL>::const_iterator itr = origins.begin();
+ itr != origins.end();
+ ++itr)
+ if (BrowsingDataHelper::HasWebScheme(*itr))
+ pending_hosts_.insert(std::make_pair(itr->host(), type));
+
+ DCHECK(type == quota::kStorageTypeTemporary ||
+ type == quota::kStorageTypePersistent);
+
+ if (type == quota::kStorageTypeTemporary) {
+ quota_manager_->GetOriginsModifiedSince(
+ quota::kStorageTypePersistent,
+ base::Time(),
+ base::Bind(&BrowsingDataQuotaHelperImpl::GotOrigins,
+ weak_factory_.GetWeakPtr()));
+ } else {
+ // type == quota::kStorageTypePersistent
+ ProcessPendingHosts();
+ }
+}
+
+void BrowsingDataQuotaHelperImpl::ProcessPendingHosts() {
+ if (pending_hosts_.empty()) {
+ OnComplete();
+ return;
+ }
+
+ PendingHosts::iterator itr = pending_hosts_.begin();
+ std::string host = itr->first;
+ quota::StorageType type = itr->second;
+ pending_hosts_.erase(itr);
+ GetHostUsage(host, type);
+}
+
+void BrowsingDataQuotaHelperImpl::GetHostUsage(const std::string& host,
+ quota::StorageType type) {
+ DCHECK(quota_manager_.get());
+ quota_manager_->GetHostUsage(
+ host, type,
+ base::Bind(&BrowsingDataQuotaHelperImpl::GotHostUsage,
+ weak_factory_.GetWeakPtr()));
+}
+
+void BrowsingDataQuotaHelperImpl::GotHostUsage(const std::string& host,
+ quota::StorageType type,
+ int64 usage) {
+ switch (type) {
+ case quota::kStorageTypeTemporary:
+ quota_info_[host].temporary_usage = usage;
+ break;
+ case quota::kStorageTypePersistent:
+ quota_info_[host].persistent_usage = usage;
+ break;
+ default:
+ NOTREACHED();
+ }
+ ProcessPendingHosts();
+}
+
+void BrowsingDataQuotaHelperImpl::OnComplete() {
+ if (!ui_thread_->BelongsToCurrentThread()) {
+ ui_thread_->PostTask(
+ FROM_HERE,
+ base::Bind(&BrowsingDataQuotaHelperImpl::OnComplete, this));
+ return;
+ }
+
+ is_fetching_ = false;
+
+ QuotaInfoArray result;
+
+ for (std::map<std::string, QuotaInfo>::iterator itr = quota_info_.begin();
+ itr != quota_info_.end();
+ ++itr) {
+ QuotaInfo* info = &itr->second;
+ // Skip unused entries
+ if (info->temporary_usage <= 0 &&
+ info->persistent_usage <= 0)
+ continue;
+
+ info->host = itr->first;
+ result.push_back(*info);
+ }
+
+ callback_.Run(result);
+ callback_.Reset();
+}
+
+void BrowsingDataQuotaHelperImpl::DidRevokeHostQuota(
+ quota::QuotaStatusCode status_unused,
+ const std::string& host_unused,
+ quota::StorageType type_unused,
+ int64 quota_unused) {
+}
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper_impl.h b/chrome/browser/browsing_data/browsing_data_quota_helper_impl.h
new file mode 100644
index 0000000..93680b5
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper_impl.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_QUOTA_HELPER_IMPL_H_
+#define CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_QUOTA_HELPER_IMPL_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time.h"
+#include "chrome/browser/browsing_data/browsing_data_quota_helper.h"
+#include "content/public/browser/browser_thread.h"
+#include "webkit/quota/quota_types.h"
+
+namespace quota {
+class QuotaManager;
+}
+
+// Implementation of BrowsingDataQuotaHelper. Since a client of
+// BrowsingDataQuotaHelper should live in UI thread and QuotaManager lives in
+// IO thread, we have to communicate over thread using PostTask.
+class BrowsingDataQuotaHelperImpl : public BrowsingDataQuotaHelper {
+ public:
+ virtual void StartFetching(const FetchResultCallback& callback) OVERRIDE;
+ virtual void RevokeHostQuota(const std::string& host) OVERRIDE;
+
+ private:
+ BrowsingDataQuotaHelperImpl(base::MessageLoopProxy* ui_thread,
+ base::MessageLoopProxy* io_thread,
+ quota::QuotaManager* quota_manager);
+ virtual ~BrowsingDataQuotaHelperImpl();
+
+ void FetchQuotaInfo();
+
+ // Callback function for GetOriginModifiedSince.
+ void GotOrigins(const std::set<GURL>& origins, quota::StorageType type);
+
+ void ProcessPendingHosts();
+ void GetHostUsage(const std::string& host, quota::StorageType type);
+
+ // Callback function for GetHostUsage.
+ void GotHostUsage(const std::string& host,
+ quota::StorageType type,
+ int64 usage);
+
+ void OnComplete();
+ void DidRevokeHostQuota(quota::QuotaStatusCode status,
+ const std::string& host,
+ quota::StorageType type,
+ int64 quota);
+
+ scoped_refptr<quota::QuotaManager> quota_manager_;
+ FetchResultCallback callback_;
+
+ typedef std::set<std::pair<std::string, quota::StorageType> > PendingHosts;
+ PendingHosts pending_hosts_;
+ std::map<std::string, QuotaInfo> quota_info_;
+
+ bool is_fetching_;
+
+ scoped_refptr<base::MessageLoopProxy> ui_thread_;
+ scoped_refptr<base::MessageLoopProxy> io_thread_;
+ base::WeakPtrFactory<BrowsingDataQuotaHelperImpl> weak_factory_;
+
+ friend class BrowsingDataQuotaHelper;
+ friend class BrowsingDataQuotaHelperTest;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataQuotaHelperImpl);
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_QUOTA_HELPER_IMPL_H_
diff --git a/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc
new file mode 100644
index 0000000..3d3b971
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_quota_helper_unittest.cc
@@ -0,0 +1,209 @@
+// Copyright (c) 2012 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 "testing/gtest/include/gtest/gtest.h"
+
+#include "base/bind.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/message_loop.h"
+#include "base/scoped_temp_dir.h"
+#include "chrome/browser/browsing_data/browsing_data_quota_helper_impl.h"
+#include "content/public/test/test_browser_thread.h"
+#include "webkit/quota/mock_storage_client.h"
+#include "webkit/quota/quota_manager.h"
+
+using content::BrowserThread;
+
+class BrowsingDataQuotaHelperTest : public testing::Test {
+ public:
+ typedef BrowsingDataQuotaHelper::QuotaInfo QuotaInfo;
+ typedef BrowsingDataQuotaHelper::QuotaInfoArray QuotaInfoArray;
+
+ BrowsingDataQuotaHelperTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_),
+ db_thread_(BrowserThread::DB, &message_loop_),
+ io_thread_(BrowserThread::IO, &message_loop_),
+ fetching_completed_(true),
+ quota_(-1),
+ weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
+
+ virtual ~BrowsingDataQuotaHelperTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ EXPECT_TRUE(dir_.CreateUniqueTempDir());
+ quota_manager_ = new quota::QuotaManager(
+ false, dir_.path(),
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB),
+ NULL);
+ helper_ = new BrowsingDataQuotaHelperImpl(
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
+ quota_manager_);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ helper_ = NULL;
+ quota_manager_ = NULL;
+ quota_info_.clear();
+ MessageLoop::current()->RunAllPending();
+ }
+
+ protected:
+ const QuotaInfoArray& quota_info() const {
+ return quota_info_;
+ }
+
+ bool fetching_completed() const {
+ return fetching_completed_;
+ }
+
+ void StartFetching() {
+ fetching_completed_ = false;
+ helper_->StartFetching(
+ base::Bind(&BrowsingDataQuotaHelperTest::FetchCompleted,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void RegisterClient(const quota::MockOriginData* data, std::size_t data_len) {
+ quota::MockStorageClient* client =
+ new quota::MockStorageClient(
+ quota_manager_->proxy(), data, quota::QuotaClient::kFileSystem,
+ data_len);
+ quota_manager_->proxy()->RegisterClient(client);
+ client->TouchAllOriginsAndNotify();
+ }
+
+ void SetPersistentHostQuota(const std::string& host, int64 quota) {
+ quota_ = -1;
+ quota_manager_->SetPersistentHostQuota(
+ host, quota,
+ base::Bind(&BrowsingDataQuotaHelperTest::GotPersistentHostQuota,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GetPersistentHostQuota(const std::string& host) {
+ quota_ = -1;
+ quota_manager_->GetPersistentHostQuota(
+ host,
+ base::Bind(&BrowsingDataQuotaHelperTest::GotPersistentHostQuota,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void GotPersistentHostQuota(quota::QuotaStatusCode status,
+ const std::string& host,
+ quota::StorageType type,
+ int64 quota) {
+ EXPECT_EQ(quota::kQuotaStatusOk, status);
+ EXPECT_EQ(quota::kStorageTypePersistent, type);
+ quota_ = quota;
+ }
+
+ void RevokeHostQuota(const std::string& host) {
+ helper_->RevokeHostQuota(host);
+ }
+
+ int64 quota() {
+ return quota_;
+ }
+
+ private:
+ void FetchCompleted(const QuotaInfoArray& quota_info) {
+ quota_info_ = quota_info;
+ fetching_completed_ = true;
+ }
+
+ MessageLoop message_loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread db_thread_;
+ content::TestBrowserThread io_thread_;
+ scoped_refptr<quota::QuotaManager> quota_manager_;
+
+ ScopedTempDir dir_;
+ scoped_refptr<BrowsingDataQuotaHelper> helper_;
+
+ bool fetching_completed_;
+ QuotaInfoArray quota_info_;
+ int64 quota_;
+ base::WeakPtrFactory<BrowsingDataQuotaHelperTest> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataQuotaHelperTest);
+};
+
+TEST_F(BrowsingDataQuotaHelperTest, Empty) {
+ StartFetching();
+ MessageLoop::current()->RunAllPending();
+ EXPECT_TRUE(fetching_completed());
+ EXPECT_TRUE(quota_info().empty());
+}
+
+TEST_F(BrowsingDataQuotaHelperTest, FetchData) {
+ const quota::MockOriginData kOrigins[] = {
+ {"http://example.com/", quota::kStorageTypeTemporary, 1},
+ {"https://example.com/", quota::kStorageTypeTemporary, 10},
+ {"http://example.com/", quota::kStorageTypePersistent, 100},
+ {"http://example2.com/", quota::kStorageTypeTemporary, 1000},
+ };
+
+ RegisterClient(kOrigins, arraysize(kOrigins));
+ StartFetching();
+ MessageLoop::current()->RunAllPending();
+ EXPECT_TRUE(fetching_completed());
+
+ std::set<QuotaInfo> expected, actual;
+ actual.insert(quota_info().begin(), quota_info().end());
+ expected.insert(QuotaInfo("example.com", 11, 100));
+ expected.insert(QuotaInfo("example2.com", 1000, 0));
+ EXPECT_TRUE(expected == actual);
+}
+
+TEST_F(BrowsingDataQuotaHelperTest, IgnoreExtensionsAndDevTools) {
+ const quota::MockOriginData kOrigins[] = {
+ {"http://example.com/", quota::kStorageTypeTemporary, 1},
+ {"https://example.com/", quota::kStorageTypeTemporary, 10},
+ {"http://example.com/", quota::kStorageTypePersistent, 100},
+ {"http://example2.com/", quota::kStorageTypeTemporary, 1000},
+ {"chrome-extension://abcdefghijklmnopqrstuvwxyz/",
+ quota::kStorageTypeTemporary, 10000},
+ {"chrome-extension://abcdefghijklmnopqrstuvwxyz/",
+ quota::kStorageTypePersistent, 100000},
+ {"chrome-devtools://abcdefghijklmnopqrstuvwxyz/",
+ quota::kStorageTypeTemporary, 10000},
+ {"chrome-devtools://abcdefghijklmnopqrstuvwxyz/",
+ quota::kStorageTypePersistent, 100000},
+ };
+
+ RegisterClient(kOrigins, arraysize(kOrigins));
+ StartFetching();
+ MessageLoop::current()->RunAllPending();
+ EXPECT_TRUE(fetching_completed());
+
+ std::set<QuotaInfo> expected, actual;
+ actual.insert(quota_info().begin(), quota_info().end());
+ expected.insert(QuotaInfo("example.com", 11, 100));
+ expected.insert(QuotaInfo("example2.com", 1000, 0));
+ EXPECT_TRUE(expected == actual);
+}
+
+TEST_F(BrowsingDataQuotaHelperTest, RevokeHostQuota) {
+ const std::string kHost1("example1.com");
+ const std::string kHost2("example2.com");
+
+ SetPersistentHostQuota(kHost1, 1);
+ SetPersistentHostQuota(kHost2, 10);
+ MessageLoop::current()->RunAllPending();
+
+ RevokeHostQuota(kHost1);
+ MessageLoop::current()->RunAllPending();
+
+ GetPersistentHostQuota(kHost1);
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(0, quota());
+
+ GetPersistentHostQuota(kHost2);
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(10, quota());
+}
diff --git a/chrome/browser/browsing_data/browsing_data_remover.cc b/chrome/browser/browsing_data/browsing_data_remover.cc
new file mode 100644
index 0000000..dc99dc7
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_remover.cc
@@ -0,0 +1,887 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_remover.h"
+
+#include <map>
+#include <set>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/platform_file.h"
+#include "chrome/browser/autofill/personal_data_manager.h"
+#include "chrome/browser/autofill/personal_data_manager_factory.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/download/download_service.h"
+#include "chrome/browser/download/download_service_factory.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_special_storage_policy.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/io_thread.h"
+#include "chrome/browser/nacl_host/nacl_browser.h"
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "chrome/browser/net/predictor.h"
+#include "chrome/browser/password_manager/password_store.h"
+#include "chrome/browser/password_manager/password_store_factory.h"
+#include "chrome/browser/prefs/pref_member.h"
+#include "chrome/browser/prerender/prerender_manager.h"
+#include "chrome/browser/prerender/prerender_manager_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/renderer_host/web_cache_manager.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/search_engines/template_url_service.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "chrome/browser/sessions/session_service_factory.h"
+#include "chrome/browser/sessions/tab_restore_service.h"
+#include "chrome/browser/sessions/tab_restore_service_factory.h"
+#include "chrome/browser/webdata/web_data_service.h"
+#include "chrome/browser/webdata/web_data_service_factory.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/dom_storage_context.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/plugin_data_remover.h"
+#include "content/public/browser/user_metrics.h"
+#include "net/base/net_errors.h"
+#include "net/base/server_bound_cert_service.h"
+#include "net/base/server_bound_cert_store.h"
+#include "net/base/transport_security_state.h"
+#include "net/cookies/cookie_store.h"
+#include "net/disk_cache/disk_cache.h"
+#include "net/http/http_cache.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "webkit/dom_storage/dom_storage_context.h"
+#include "webkit/quota/quota_manager.h"
+#include "webkit/quota/quota_types.h"
+#include "webkit/quota/special_storage_policy.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+using content::DOMStorageContext;
+using content::DownloadManager;
+using content::UserMetricsAction;
+
+bool BrowsingDataRemover::removing_ = false;
+
+BrowsingDataRemover::NotificationDetails::NotificationDetails()
+ : removal_begin(base::Time()),
+ removal_mask(-1),
+ origin_set_mask(-1) {
+}
+
+BrowsingDataRemover::NotificationDetails::NotificationDetails(
+ const BrowsingDataRemover::NotificationDetails& details)
+ : removal_begin(details.removal_begin),
+ removal_mask(details.removal_mask),
+ origin_set_mask(details.origin_set_mask) {
+}
+
+BrowsingDataRemover::NotificationDetails::NotificationDetails(
+ base::Time removal_begin,
+ int removal_mask,
+ int origin_set_mask)
+ : removal_begin(removal_begin),
+ removal_mask(removal_mask),
+ origin_set_mask(origin_set_mask) {
+}
+
+BrowsingDataRemover::NotificationDetails::~NotificationDetails() {}
+
+// TODO(mkwst): We should have one constructor, not two. http://crbug.com/130732
+BrowsingDataRemover::BrowsingDataRemover(Profile* profile,
+ base::Time delete_begin,
+ base::Time delete_end)
+ : profile_(profile),
+ quota_manager_(NULL),
+ dom_storage_context_(NULL),
+ special_storage_policy_(profile->GetExtensionSpecialStoragePolicy()),
+ delete_begin_(delete_begin),
+ delete_end_(delete_end),
+ next_cache_state_(STATE_NONE),
+ cache_(NULL),
+ main_context_getter_(profile->GetRequestContext()),
+ media_context_getter_(profile->GetRequestContextForMedia()),
+ deauthorize_content_licenses_request_id_(0),
+ waiting_for_clear_cache_(false),
+ waiting_for_clear_nacl_cache_(false),
+ waiting_for_clear_cookies_count_(0),
+ waiting_for_clear_history_(false),
+ waiting_for_clear_local_storage_(false),
+ waiting_for_clear_networking_history_(false),
+ waiting_for_clear_server_bound_certs_(false),
+ waiting_for_clear_plugin_data_(false),
+ waiting_for_clear_quota_managed_data_(false),
+ waiting_for_clear_content_licenses_(false),
+ remove_mask_(0),
+ remove_origin_(GURL()),
+ origin_set_mask_(0) {
+ DCHECK(profile);
+}
+
+BrowsingDataRemover::BrowsingDataRemover(Profile* profile,
+ TimePeriod time_period,
+ base::Time delete_end)
+ : profile_(profile),
+ quota_manager_(NULL),
+ dom_storage_context_(NULL),
+ special_storage_policy_(profile->GetExtensionSpecialStoragePolicy()),
+ delete_begin_(CalculateBeginDeleteTime(time_period)),
+ delete_end_(delete_end),
+ next_cache_state_(STATE_NONE),
+ cache_(NULL),
+ main_context_getter_(profile->GetRequestContext()),
+ media_context_getter_(profile->GetRequestContextForMedia()),
+ deauthorize_content_licenses_request_id_(0),
+ waiting_for_clear_cache_(false),
+ waiting_for_clear_nacl_cache_(false),
+ waiting_for_clear_cookies_count_(0),
+ waiting_for_clear_history_(false),
+ waiting_for_clear_local_storage_(false),
+ waiting_for_clear_networking_history_(false),
+ waiting_for_clear_server_bound_certs_(false),
+ waiting_for_clear_plugin_data_(false),
+ waiting_for_clear_quota_managed_data_(false),
+ waiting_for_clear_content_licenses_(false),
+ remove_mask_(0),
+ remove_origin_(GURL()),
+ origin_set_mask_(0) {
+ DCHECK(profile);
+}
+
+BrowsingDataRemover::~BrowsingDataRemover() {
+ DCHECK(AllDone());
+}
+
+// Static.
+void BrowsingDataRemover::set_removing(bool removing) {
+ DCHECK(removing_ != removing);
+ removing_ = removing;
+}
+
+// Static.
+int BrowsingDataRemover::GenerateQuotaClientMask(int remove_mask) {
+ int quota_client_mask = 0;
+ if (remove_mask & BrowsingDataRemover::REMOVE_FILE_SYSTEMS)
+ quota_client_mask |= quota::QuotaClient::kFileSystem;
+ if (remove_mask & BrowsingDataRemover::REMOVE_WEBSQL)
+ quota_client_mask |= quota::QuotaClient::kDatabase;
+ if (remove_mask & BrowsingDataRemover::REMOVE_APPCACHE)
+ quota_client_mask |= quota::QuotaClient::kAppcache;
+ if (remove_mask & BrowsingDataRemover::REMOVE_INDEXEDDB)
+ quota_client_mask |= quota::QuotaClient::kIndexedDatabase;
+
+ return quota_client_mask;
+}
+
+void BrowsingDataRemover::Remove(int remove_mask, int origin_set_mask) {
+ RemoveImpl(remove_mask, GURL(), origin_set_mask);
+}
+
+void BrowsingDataRemover::RemoveImpl(int remove_mask,
+ const GURL& origin,
+ int origin_set_mask) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ set_removing(true);
+ remove_mask_ = remove_mask;
+ remove_origin_ = origin;
+ origin_set_mask_ = origin_set_mask;
+
+ if (origin_set_mask_ & BrowsingDataHelper::UNPROTECTED_WEB) {
+ content::RecordAction(
+ UserMetricsAction("ClearBrowsingData_MaskContainsUnprotectedWeb"));
+ }
+ if (origin_set_mask_ & BrowsingDataHelper::PROTECTED_WEB) {
+ content::RecordAction(
+ UserMetricsAction("ClearBrowsingData_MaskContainsProtectedWeb"));
+ }
+ if (origin_set_mask_ & BrowsingDataHelper::EXTENSION) {
+ content::RecordAction(
+ UserMetricsAction("ClearBrowsingData_MaskContainsExtension"));
+ }
+ // If this fires, we added a new BrowsingDataHelper::OriginSetMask without
+ // updating the user metrics above.
+ COMPILE_ASSERT(
+ BrowsingDataHelper::ALL == (BrowsingDataHelper::UNPROTECTED_WEB |
+ BrowsingDataHelper::PROTECTED_WEB |
+ BrowsingDataHelper::EXTENSION),
+ forgotten_to_add_origin_mask_type);
+
+ if (remove_mask & REMOVE_HISTORY) {
+ HistoryService* history_service = HistoryServiceFactory::GetForProfile(
+ profile_, Profile::EXPLICIT_ACCESS);
+ if (history_service) {
+ std::set<GURL> restrict_urls;
+ if (!remove_origin_.is_empty())
+ restrict_urls.insert(remove_origin_);
+ content::RecordAction(UserMetricsAction("ClearBrowsingData_History"));
+ waiting_for_clear_history_ = true;
+ history_service->ExpireHistoryBetween(restrict_urls,
+ delete_begin_, delete_end_,
+ &request_consumer_,
+ base::Bind(&BrowsingDataRemover::OnHistoryDeletionDone,
+ base::Unretained(this)));
+ }
+
+ // Need to clear the host cache and accumulated speculative data, as it also
+ // reveals some history: we have no mechanism to track when these items were
+ // created, so we'll clear them all. Better safe than sorry.
+ if (g_browser_process->io_thread()) {
+ waiting_for_clear_networking_history_ = true;
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrowsingDataRemover::ClearNetworkingHistory,
+ base::Unretained(this), g_browser_process->io_thread()));
+ }
+
+ // As part of history deletion we also delete the auto-generated keywords.
+ TemplateURLService* keywords_model =
+ TemplateURLServiceFactory::GetForProfile(profile_);
+ if (keywords_model && !keywords_model->loaded()) {
+ registrar_.Add(this, chrome::NOTIFICATION_TEMPLATE_URL_SERVICE_LOADED,
+ content::Source<TemplateURLService>(keywords_model));
+ keywords_model->Load();
+ } else if (keywords_model) {
+ keywords_model->RemoveAutoGeneratedForOriginBetween(remove_origin_,
+ delete_begin_, delete_end_);
+ }
+
+ // The PrerenderManager keeps history of prerendered pages, so clear that.
+ // It also may have a prerendered page. If so, the page could be
+ // considered to have a small amount of historical information, so delete
+ // it, too.
+ prerender::PrerenderManager* prerender_manager =
+ prerender::PrerenderManagerFactory::GetForProfile(profile_);
+ if (prerender_manager) {
+ prerender_manager->ClearData(
+ prerender::PrerenderManager::CLEAR_PRERENDER_CONTENTS |
+ prerender::PrerenderManager::CLEAR_PRERENDER_HISTORY);
+ }
+
+ // If the caller is removing history for all hosts, then clear ancillary
+ // historical information.
+ if (remove_origin_.is_empty()) {
+ // We also delete the list of recently closed tabs. Since these expire,
+ // they can't be more than a day old, so we can simply clear them all.
+ TabRestoreService* tab_service =
+ TabRestoreServiceFactory::GetForProfile(profile_);
+ if (tab_service) {
+ tab_service->ClearEntries();
+ tab_service->DeleteLastSession();
+ }
+
+#if defined(ENABLE_SESSION_SERVICE)
+ // We also delete the last session when we delete the history.
+ SessionService* session_service =
+ SessionServiceFactory::GetForProfile(profile_);
+ if (session_service)
+ session_service->DeleteLastSession();
+#endif
+ }
+ }
+
+ if (remove_mask & REMOVE_DOWNLOADS) {
+ content::RecordAction(UserMetricsAction("ClearBrowsingData_Downloads"));
+ DownloadManager* download_manager =
+ BrowserContext::GetDownloadManager(profile_);
+ download_manager->RemoveDownloadsBetween(delete_begin_, delete_end_);
+ download_manager->ClearLastDownloadPath();
+ }
+
+ // We ignore the REMOVE_COOKIES request if UNPROTECTED_WEB is not set,
+ // so that callers who request REMOVE_SITE_DATA with PROTECTED_WEB
+ // don't accidentally remove the cookies that are associated with the
+ // UNPROTECTED_WEB origin. This is necessary because cookies are not separated
+ // between UNPROTECTED_WEB and PROTECTED_WEB.
+ if (remove_mask & REMOVE_COOKIES &&
+ origin_set_mask_ & BrowsingDataHelper::UNPROTECTED_WEB) {
+ content::RecordAction(UserMetricsAction("ClearBrowsingData_Cookies"));
+ // Since we are running on the UI thread don't call GetURLRequestContext().
+ net::URLRequestContextGetter* rq_context = profile_->GetRequestContext();
+ if (rq_context) {
+ ++waiting_for_clear_cookies_count_;
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrowsingDataRemover::ClearCookiesOnIOThread,
+ base::Unretained(this), base::Unretained(rq_context)));
+ }
+
+#if defined(ENABLE_SAFE_BROWSING)
+ // Clear the safebrowsing cookies only if time period is for "all time". It
+ // doesn't make sense to apply the time period of deleting in the last X
+ // hours/days to the safebrowsing cookies since they aren't the result of
+ // any user action.
+ if (delete_begin_ == base::Time()) {
+ SafeBrowsingService* sb_service =
+ g_browser_process->safe_browsing_service();
+ if (sb_service) {
+ net::URLRequestContextGetter* sb_context =
+ sb_service->url_request_context();
+ ++waiting_for_clear_cookies_count_;
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrowsingDataRemover::ClearCookiesOnIOThread,
+ base::Unretained(this), base::Unretained(sb_context)));
+ }
+ }
+#endif
+ }
+
+ // Server bound certs are not separated for protected and unprotected web
+ // origins. We check the origin_set_mask_ to prevent unintended deletion.
+ if (remove_mask & REMOVE_SERVER_BOUND_CERTS &&
+ origin_set_mask_ & BrowsingDataHelper::UNPROTECTED_WEB) {
+ content::RecordAction(
+ UserMetricsAction("ClearBrowsingData_ServerBoundCerts"));
+ // Since we are running on the UI thread don't call GetURLRequestContext().
+ net::URLRequestContextGetter* rq_context = profile_->GetRequestContext();
+ if (rq_context) {
+ waiting_for_clear_server_bound_certs_ = true;
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrowsingDataRemover::ClearServerBoundCertsOnIOThread,
+ base::Unretained(this), base::Unretained(rq_context)));
+ }
+ }
+
+ if (remove_mask & REMOVE_LOCAL_STORAGE) {
+ waiting_for_clear_local_storage_ = true;
+ if (!dom_storage_context_)
+ dom_storage_context_ =
+ BrowserContext::GetDefaultDOMStorageContext(profile_);
+ ClearLocalStorageOnUIThread();
+ }
+
+ if (remove_mask & REMOVE_INDEXEDDB || remove_mask & REMOVE_WEBSQL ||
+ remove_mask & REMOVE_APPCACHE || remove_mask & REMOVE_FILE_SYSTEMS) {
+ if (!quota_manager_)
+ quota_manager_ = content::BrowserContext::GetQuotaManager(profile_);
+ waiting_for_clear_quota_managed_data_ = true;
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrowsingDataRemover::ClearQuotaManagedDataOnIOThread,
+ base::Unretained(this)));
+ }
+
+ // Plugin is data not separated for protected and unprotected web origins. We
+ // check the origin_set_mask_ to prevent unintended deletion.
+ if (remove_mask & REMOVE_PLUGIN_DATA &&
+ origin_set_mask_ & BrowsingDataHelper::UNPROTECTED_WEB) {
+ content::RecordAction(UserMetricsAction("ClearBrowsingData_LSOData"));
+
+ waiting_for_clear_plugin_data_ = true;
+ if (!plugin_data_remover_.get())
+ plugin_data_remover_.reset(content::PluginDataRemover::Create(profile_));
+ base::WaitableEvent* event =
+ plugin_data_remover_->StartRemoving(delete_begin_);
+ watcher_.StartWatching(event, this);
+ }
+
+ if (remove_mask & REMOVE_PASSWORDS) {
+ content::RecordAction(UserMetricsAction("ClearBrowsingData_Passwords"));
+ PasswordStore* password_store = PasswordStoreFactory::GetForProfile(
+ profile_, Profile::EXPLICIT_ACCESS);
+
+ if (password_store)
+ password_store->RemoveLoginsCreatedBetween(delete_begin_, delete_end_);
+ }
+
+ if (remove_mask & REMOVE_FORM_DATA) {
+ content::RecordAction(UserMetricsAction("ClearBrowsingData_Autofill"));
+ scoped_refptr<WebDataService> web_data_service =
+ WebDataServiceFactory::GetForProfile(profile_,
+ Profile::EXPLICIT_ACCESS);
+
+ if (web_data_service.get()) {
+ web_data_service->RemoveFormElementsAddedBetween(delete_begin_,
+ delete_end_);
+ web_data_service->RemoveAutofillProfilesAndCreditCardsModifiedBetween(
+ delete_begin_, delete_end_);
+ PersonalDataManager* data_manager =
+ PersonalDataManagerFactory::GetForProfile(profile_);
+ if (data_manager) {
+ data_manager->Refresh();
+ }
+ }
+ }
+
+ if (remove_mask & REMOVE_CACHE) {
+ // Tell the renderers to clear their cache.
+ WebCacheManager::GetInstance()->ClearCache();
+
+ // Invoke DoClearCache on the IO thread.
+ waiting_for_clear_cache_ = true;
+ content::RecordAction(UserMetricsAction("ClearBrowsingData_Cache"));
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrowsingDataRemover::ClearCacheOnIOThread,
+ base::Unretained(this)));
+
+#if !defined(DISABLE_NACL)
+ waiting_for_clear_nacl_cache_ = true;
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrowsingDataRemover::ClearNaClCacheOnIOThread,
+ base::Unretained(this)));
+#endif
+
+ // The PrerenderManager may have a page actively being prerendered, which
+ // is essentially a preemptively cached page.
+ prerender::PrerenderManager* prerender_manager =
+ prerender::PrerenderManagerFactory::GetForProfile(profile_);
+ if (prerender_manager) {
+ prerender_manager->ClearData(
+ prerender::PrerenderManager::CLEAR_PRERENDER_CONTENTS);
+ }
+ }
+
+ if (remove_mask & REMOVE_CONTENT_LICENSES) {
+ content::RecordAction(
+ UserMetricsAction("ClearBrowsingData_ContentLicenses"));
+
+ waiting_for_clear_content_licenses_ = true;
+ if (!pepper_flash_settings_manager_.get()) {
+ pepper_flash_settings_manager_.reset(
+ new PepperFlashSettingsManager(this, profile_));
+ }
+ deauthorize_content_licenses_request_id_ =
+ pepper_flash_settings_manager_->DeauthorizeContentLicenses();
+ }
+
+ // Also delete cached network related data (like TransportSecurityState,
+ // HttpServerProperties data).
+ profile_->ClearNetworkingHistorySince(delete_begin_);
+
+ NotifyAndDeleteIfDone();
+}
+
+void BrowsingDataRemover::AddObserver(Observer* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void BrowsingDataRemover::RemoveObserver(Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+void BrowsingDataRemover::OnHistoryDeletionDone() {
+ waiting_for_clear_history_ = false;
+ NotifyAndDeleteIfDone();
+}
+
+void BrowsingDataRemover::OverrideQuotaManagerForTesting(
+ quota::QuotaManager* quota_manager) {
+ quota_manager_ = quota_manager;
+}
+
+base::Time BrowsingDataRemover::CalculateBeginDeleteTime(
+ TimePeriod time_period) {
+ base::TimeDelta diff;
+ base::Time delete_begin_time = base::Time::Now();
+ switch (time_period) {
+ case LAST_HOUR:
+ diff = base::TimeDelta::FromHours(1);
+ break;
+ case LAST_DAY:
+ diff = base::TimeDelta::FromHours(24);
+ break;
+ case LAST_WEEK:
+ diff = base::TimeDelta::FromHours(7*24);
+ break;
+ case FOUR_WEEKS:
+ diff = base::TimeDelta::FromHours(4*7*24);
+ break;
+ case EVERYTHING:
+ delete_begin_time = base::Time();
+ break;
+ default:
+ NOTREACHED() << L"Missing item";
+ break;
+ }
+ return delete_begin_time - diff;
+}
+
+bool BrowsingDataRemover::AllDone() {
+ return registrar_.IsEmpty() &&
+ !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_networking_history_ &&
+ !waiting_for_clear_server_bound_certs_ &&
+ !waiting_for_clear_plugin_data_ &&
+ !waiting_for_clear_quota_managed_data_ &&
+ !waiting_for_clear_content_licenses_;
+}
+
+void BrowsingDataRemover::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ // TODO(brettw) bug 1139736: This should also observe session
+ // clearing (what about other things such as passwords, etc.?) and wait for
+ // them to complete before continuing.
+ DCHECK(type == chrome::NOTIFICATION_TEMPLATE_URL_SERVICE_LOADED);
+ TemplateURLService* model = content::Source<TemplateURLService>(source).ptr();
+ if (model->profile() == profile_) {
+ registrar_.RemoveAll();
+ model->RemoveAutoGeneratedBetween(delete_begin_, delete_end_);
+ NotifyAndDeleteIfDone();
+ }
+}
+
+void BrowsingDataRemover::NotifyAndDeleteIfDone() {
+ // TODO(brettw) bug 1139736: see TODO in Observe() above.
+ if (!AllDone())
+ return;
+
+ set_removing(false);
+
+ // Send global notification, then notify any explicit observers.
+ BrowsingDataRemover::NotificationDetails details(delete_begin_, remove_mask_,
+ origin_set_mask_);
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_BROWSING_DATA_REMOVED,
+ content::Source<Profile>(profile_),
+ content::Details<BrowsingDataRemover::NotificationDetails>(&details));
+
+ FOR_EACH_OBSERVER(Observer, observer_list_, OnBrowsingDataRemoverDone());
+
+ // History requests aren't happy if you delete yourself from the callback.
+ // As such, we do a delete later.
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+void BrowsingDataRemover::ClearedNetworkHistory() {
+ waiting_for_clear_networking_history_ = false;
+
+ NotifyAndDeleteIfDone();
+}
+
+void BrowsingDataRemover::ClearNetworkingHistory(IOThread* io_thread) {
+ // This function should be called on the IO thread.
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ io_thread->ClearHostCache();
+
+ chrome_browser_net::Predictor* predictor = profile_->GetNetworkPredictor();
+ if (predictor) {
+ predictor->DiscardInitialNavigationHistory();
+ predictor->DiscardAllResults();
+ }
+
+ // Notify the UI thread that we are done.
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&BrowsingDataRemover::ClearedNetworkHistory,
+ base::Unretained(this)));
+}
+
+void BrowsingDataRemover::ClearedCache() {
+ waiting_for_clear_cache_ = false;
+
+ NotifyAndDeleteIfDone();
+}
+
+void BrowsingDataRemover::ClearCacheOnIOThread() {
+ // This function should be called on the IO thread.
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK_EQ(STATE_NONE, next_cache_state_);
+ DCHECK(main_context_getter_);
+ DCHECK(media_context_getter_);
+
+ next_cache_state_ = STATE_CREATE_MAIN;
+ DoClearCache(net::OK);
+}
+
+// The expected state sequence is STATE_NONE --> STATE_CREATE_MAIN -->
+// STATE_DELETE_MAIN --> STATE_CREATE_MEDIA --> STATE_DELETE_MEDIA -->
+// STATE_DONE, and any errors are ignored.
+void BrowsingDataRemover::DoClearCache(int rv) {
+ DCHECK_NE(STATE_NONE, next_cache_state_);
+
+ while (rv != net::ERR_IO_PENDING && next_cache_state_ != STATE_NONE) {
+ switch (next_cache_state_) {
+ case STATE_CREATE_MAIN:
+ case STATE_CREATE_MEDIA: {
+ // Get a pointer to the cache.
+ net::URLRequestContextGetter* getter =
+ (next_cache_state_ == STATE_CREATE_MAIN) ?
+ main_context_getter_ : media_context_getter_;
+ net::HttpTransactionFactory* factory =
+ getter->GetURLRequestContext()->http_transaction_factory();
+
+ rv = factory->GetCache()->GetBackend(
+ &cache_, base::Bind(&BrowsingDataRemover::DoClearCache,
+ base::Unretained(this)));
+ next_cache_state_ = (next_cache_state_ == STATE_CREATE_MAIN) ?
+ STATE_DELETE_MAIN : STATE_DELETE_MEDIA;
+ break;
+ }
+ case STATE_DELETE_MAIN:
+ case STATE_DELETE_MEDIA: {
+ // |cache_| can be null if it cannot be initialized.
+ if (cache_) {
+ if (delete_begin_.is_null()) {
+ rv = cache_->DoomAllEntries(
+ base::Bind(&BrowsingDataRemover::DoClearCache,
+ base::Unretained(this)));
+ } else {
+ rv = cache_->DoomEntriesBetween(
+ delete_begin_, delete_end_,
+ base::Bind(&BrowsingDataRemover::DoClearCache,
+ base::Unretained(this)));
+ }
+ cache_ = NULL;
+ }
+ next_cache_state_ = (next_cache_state_ == STATE_DELETE_MAIN) ?
+ STATE_CREATE_MEDIA : STATE_DONE;
+ break;
+ }
+ case STATE_DONE: {
+ cache_ = NULL;
+
+ // Notify the UI thread that we are done.
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&BrowsingDataRemover::ClearedCache,
+ base::Unretained(this)));
+
+ next_cache_state_ = STATE_NONE;
+ break;
+ }
+ default: {
+ NOTREACHED() << "bad state";
+ next_cache_state_ = STATE_NONE; // Stop looping.
+ break;
+ }
+ }
+ }
+}
+
+#if !defined(DISABLE_NACL)
+void BrowsingDataRemover::ClearedNaClCache() {
+ // This function should be called on the UI thread.
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ waiting_for_clear_nacl_cache_ = false;
+
+ NotifyAndDeleteIfDone();
+}
+
+void BrowsingDataRemover::ClearedNaClCacheOnIOThread() {
+ // This function should be called on the IO thread.
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // Notify the UI thread that we are done.
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&BrowsingDataRemover::ClearedNaClCache,
+ base::Unretained(this)));
+}
+
+void BrowsingDataRemover::ClearNaClCacheOnIOThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ NaClBrowser::GetInstance()->ClearValidationCache(
+ base::Bind(&BrowsingDataRemover::ClearedNaClCacheOnIOThread,
+ base::Unretained(this)));
+}
+#endif
+
+void BrowsingDataRemover::ClearLocalStorageOnUIThread() {
+ DCHECK(waiting_for_clear_local_storage_);
+
+ dom_storage_context_->GetUsageInfo(
+ base::Bind(&BrowsingDataRemover::OnGotLocalStorageUsageInfo,
+ base::Unretained(this)));
+}
+
+void BrowsingDataRemover::OnGotLocalStorageUsageInfo(
+ const std::vector<dom_storage::DomStorageContext::UsageInfo>& infos) {
+ DCHECK(waiting_for_clear_local_storage_);
+
+ for (size_t i = 0; i < infos.size(); ++i) {
+ if (!BrowsingDataHelper::DoesOriginMatchMask(infos[i].origin,
+ origin_set_mask_,
+ special_storage_policy_))
+ continue;
+
+ if (infos[i].last_modified >= delete_begin_ &&
+ infos[i].last_modified <= delete_end_)
+ dom_storage_context_->DeleteOrigin(infos[i].origin);
+ }
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&BrowsingDataRemover::OnLocalStorageCleared,
+ base::Unretained(this)));
+}
+
+void BrowsingDataRemover::OnLocalStorageCleared() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(waiting_for_clear_local_storage_);
+ waiting_for_clear_local_storage_ = false;
+ NotifyAndDeleteIfDone();
+}
+
+void BrowsingDataRemover::ClearQuotaManagedDataOnIOThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // Ask the QuotaManager for all origins with temporary quota modified within
+ // the user-specified timeframe, and deal with the resulting set in
+ // OnGotQuotaManagedOrigins().
+ quota_managed_origins_to_delete_count_ = 0;
+ quota_managed_storage_types_to_delete_count_ = 0;
+
+ if (delete_begin_ == base::Time() ||
+ origin_set_mask_ &
+ (BrowsingDataHelper::PROTECTED_WEB | BrowsingDataHelper::EXTENSION)) {
+ // If we're deleting since the beginning of time, or we're removing
+ // protected origins, then ask the QuotaManager for all origins with
+ // persistent quota modified within the user-specified timeframe, and deal
+ // with the resulting set in OnGotQuotaManagedOrigins.
+ ++quota_managed_storage_types_to_delete_count_;
+ quota_manager_->GetOriginsModifiedSince(
+ quota::kStorageTypePersistent, delete_begin_,
+ base::Bind(&BrowsingDataRemover::OnGotQuotaManagedOrigins,
+ base::Unretained(this)));
+ }
+
+ // Do the same for temporary quota.
+ ++quota_managed_storage_types_to_delete_count_;
+ quota_manager_->GetOriginsModifiedSince(
+ quota::kStorageTypeTemporary, delete_begin_,
+ base::Bind(&BrowsingDataRemover::OnGotQuotaManagedOrigins,
+ base::Unretained(this)));
+}
+
+void BrowsingDataRemover::OnGotQuotaManagedOrigins(
+ const std::set<GURL>& origins, quota::StorageType type) {
+ DCHECK_GT(quota_managed_storage_types_to_delete_count_, 0);
+ // Walk through the origins passed in, delete quota of |type| from each that
+ // matches the |origin_set_mask_|.
+ std::set<GURL>::const_iterator origin;
+ for (origin = origins.begin(); origin != origins.end(); ++origin) {
+ // TODO(mkwst): Clean this up, it's slow. http://crbug.com/130746
+ if (!remove_origin_.is_empty() && remove_origin_ != origin->GetOrigin())
+ continue;
+
+ if (!BrowsingDataHelper::DoesOriginMatchMask(origin->GetOrigin(),
+ origin_set_mask_,
+ special_storage_policy_))
+ continue;
+
+ ++quota_managed_origins_to_delete_count_;
+ quota_manager_->DeleteOriginData(
+ origin->GetOrigin(), type,
+ BrowsingDataRemover::GenerateQuotaClientMask(remove_mask_),
+ base::Bind(&BrowsingDataRemover::OnQuotaManagedOriginDeletion,
+ base::Unretained(this), origin->GetOrigin(), type));
+ }
+
+ --quota_managed_storage_types_to_delete_count_;
+ CheckQuotaManagedDataDeletionStatus();
+}
+
+void BrowsingDataRemover::OnQuotaManagedOriginDeletion(
+ const GURL& origin,
+ quota::StorageType type,
+ quota::QuotaStatusCode status) {
+ DCHECK_GT(quota_managed_origins_to_delete_count_, 0);
+ if (status != quota::kQuotaStatusOk) {
+ DLOG(ERROR) << "Couldn't remove data of type " << type << " for origin "
+ << origin << ". Status: " << status;
+ }
+
+ --quota_managed_origins_to_delete_count_;
+ CheckQuotaManagedDataDeletionStatus();
+}
+
+void BrowsingDataRemover::CheckQuotaManagedDataDeletionStatus() {
+ if (quota_managed_storage_types_to_delete_count_ != 0 ||
+ quota_managed_origins_to_delete_count_ != 0) {
+ return;
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&BrowsingDataRemover::OnQuotaManagedDataDeleted,
+ base::Unretained(this)));
+}
+
+void BrowsingDataRemover::OnQuotaManagedDataDeleted() {
+ DCHECK(waiting_for_clear_quota_managed_data_);
+ waiting_for_clear_quota_managed_data_ = false;
+ NotifyAndDeleteIfDone();
+}
+
+void BrowsingDataRemover::OnWaitableEventSignaled(
+ base::WaitableEvent* waitable_event) {
+ waiting_for_clear_plugin_data_ = false;
+ NotifyAndDeleteIfDone();
+}
+
+void BrowsingDataRemover::OnDeauthorizeContentLicensesCompleted(
+ uint32 request_id,
+ bool /* success */) {
+ DCHECK(waiting_for_clear_content_licenses_);
+ DCHECK_EQ(request_id, deauthorize_content_licenses_request_id_);
+
+ waiting_for_clear_content_licenses_ = false;
+ NotifyAndDeleteIfDone();
+}
+
+void BrowsingDataRemover::OnClearedCookies(int num_deleted) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&BrowsingDataRemover::OnClearedCookies,
+ base::Unretained(this), num_deleted));
+ return;
+ }
+
+ DCHECK(waiting_for_clear_cookies_count_ > 0);
+ --waiting_for_clear_cookies_count_;
+ NotifyAndDeleteIfDone();
+}
+
+void BrowsingDataRemover::ClearCookiesOnIOThread(
+ net::URLRequestContextGetter* rq_context) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ net::CookieStore* cookie_store = rq_context->
+ GetURLRequestContext()->cookie_store();
+ cookie_store->DeleteAllCreatedBetweenAsync(
+ delete_begin_, delete_end_,
+ base::Bind(&BrowsingDataRemover::OnClearedCookies,
+ base::Unretained(this)));
+}
+
+void BrowsingDataRemover::ClearServerBoundCertsOnIOThread(
+ net::URLRequestContextGetter* rq_context) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ net::ServerBoundCertService* server_bound_cert_service =
+ rq_context->GetURLRequestContext()->server_bound_cert_service();
+ server_bound_cert_service->GetCertStore()->DeleteAllCreatedBetween(
+ delete_begin_, delete_end_);
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&BrowsingDataRemover::OnClearedServerBoundCerts,
+ base::Unretained(this)));
+}
+
+void BrowsingDataRemover::OnClearedServerBoundCerts() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ waiting_for_clear_server_bound_certs_ = false;
+ NotifyAndDeleteIfDone();
+}
diff --git a/chrome/browser/browsing_data/browsing_data_remover.h b/chrome/browser/browsing_data/browsing_data_remover.h
new file mode 100644
index 0000000..330bafd
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_remover.h
@@ -0,0 +1,367 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_REMOVER_H_
+#define CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_REMOVER_H_
+
+#include <set>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "base/synchronization/waitable_event_watcher.h"
+#include "base/time.h"
+#include "chrome/browser/cancelable_request.h"
+#include "chrome/browser/pepper_flash_settings_manager.h"
+#include "chrome/browser/prefs/pref_member.h"
+#include "content/public/browser/dom_storage_context.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/dom_storage/dom_storage_context.h"
+#include "webkit/quota/quota_types.h"
+
+class ExtensionSpecialStoragePolicy;
+class IOThread;
+class Profile;
+
+namespace content {
+class PluginDataRemover;
+}
+
+namespace disk_cache {
+class Backend;
+}
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace quota {
+class QuotaManager;
+}
+
+// BrowsingDataRemover is responsible for removing data related to browsing:
+// visits in url database, downloads, cookies ...
+
+class BrowsingDataRemover : public content::NotificationObserver,
+ public base::WaitableEventWatcher::Delegate,
+ public PepperFlashSettingsManager::Client {
+ public:
+ // Time period ranges available when doing browsing data removals.
+ enum TimePeriod {
+ LAST_HOUR = 0,
+ LAST_DAY,
+ LAST_WEEK,
+ FOUR_WEEKS,
+ EVERYTHING
+ };
+
+ // Mask used for Remove.
+ enum RemoveDataMask {
+ REMOVE_APPCACHE = 1 << 0,
+ REMOVE_CACHE = 1 << 1,
+ REMOVE_COOKIES = 1 << 2,
+ REMOVE_DOWNLOADS = 1 << 3,
+ REMOVE_FILE_SYSTEMS = 1 << 4,
+ REMOVE_FORM_DATA = 1 << 5,
+ // In addition to visits, REMOVE_HISTORY removes keywords and last session.
+ REMOVE_HISTORY = 1 << 6,
+ REMOVE_INDEXEDDB = 1 << 7,
+ REMOVE_LOCAL_STORAGE = 1 << 8,
+ REMOVE_PLUGIN_DATA = 1 << 9,
+ REMOVE_PASSWORDS = 1 << 10,
+ REMOVE_WEBSQL = 1 << 11,
+ REMOVE_SERVER_BOUND_CERTS = 1 << 12,
+ REMOVE_CONTENT_LICENSES = 1 << 13,
+
+ // "Site data" includes cookies, appcache, file systems, indexedDBs, local
+ // storage, webSQL, and plugin data.
+ REMOVE_SITE_DATA = REMOVE_APPCACHE | REMOVE_COOKIES | REMOVE_FILE_SYSTEMS |
+ REMOVE_INDEXEDDB | REMOVE_LOCAL_STORAGE |
+ REMOVE_PLUGIN_DATA | REMOVE_WEBSQL |
+ REMOVE_SERVER_BOUND_CERTS
+ };
+
+ // When BrowsingDataRemover successfully removes data, a notification of type
+ // NOTIFICATION_BROWSING_DATA_REMOVED is triggered with a Details object of
+ // this type.
+ struct NotificationDetails {
+ NotificationDetails();
+ NotificationDetails(const NotificationDetails& details);
+ NotificationDetails(base::Time removal_begin,
+ int removal_mask,
+ int origin_set_mask);
+ ~NotificationDetails();
+
+ // The beginning of the removal time range.
+ base::Time removal_begin;
+
+ // The removal mask (see the RemoveDataMask enum for details).
+ int removal_mask;
+
+ // The origin set mask (see BrowsingDataHelper::OriginSetMask for details).
+ int origin_set_mask;
+ };
+
+ // Observer is notified when the removal is done. Done means keywords have
+ // been deleted, cache cleared and all other tasks scheduled.
+ class Observer {
+ public:
+ virtual void OnBrowsingDataRemoverDone() = 0;
+
+ protected:
+ virtual ~Observer() {}
+ };
+
+ // Creates a BrowsingDataRemover to remove browser data from the specified
+ // profile in the specified time range. Use Remove to initiate the removal.
+ BrowsingDataRemover(Profile* profile, base::Time delete_begin,
+ base::Time delete_end);
+
+ // Creates a BrowsingDataRemover to remove browser data from the specified
+ // profile in the specified time range.
+ BrowsingDataRemover(Profile* profile, TimePeriod time_period,
+ base::Time delete_end);
+
+ // Removes the specified items related to browsing for all origins that match
+ // the provided |origin_set_mask| (see BrowsingDataHelper::OriginSetMask).
+ void Remove(int remove_mask, int origin_set_mask);
+
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ // Called when history deletion is done.
+ void OnHistoryDeletionDone();
+
+ // Quota managed data uses a different bitmask for types than
+ // BrowsingDataRemover uses. This method generates that mask.
+ static int GenerateQuotaClientMask(int remove_mask);
+
+ // Used for testing.
+ void OverrideQuotaManagerForTesting(quota::QuotaManager* quota_manager);
+
+ static bool is_removing() { return removing_; }
+
+ private:
+ // The clear API needs to be able to toggle removing_ in order to test that
+ // only one BrowsingDataRemover instance can be called at a time.
+ FRIEND_TEST_ALL_PREFIXES(ExtensionBrowsingDataTest, OneAtATime);
+
+ // The BrowsingDataRemover tests need to be able to access the implementation
+ // of Remove(), as it exposes details that aren't yet available in the public
+ // API. As soon as those details are exposed via new methods, this should be
+ // removed.
+ //
+ // TODO(mkwst): See http://crbug.com/113621
+ friend class BrowsingDataRemoverTest;
+
+ enum CacheState {
+ STATE_NONE,
+ STATE_CREATE_MAIN,
+ STATE_CREATE_MEDIA,
+ STATE_DELETE_MAIN,
+ STATE_DELETE_MEDIA,
+ STATE_DONE
+ };
+
+ // BrowsingDataRemover deletes itself (using DeleteHelper) and is not supposed
+ // to be deleted by other objects so make destructor private and DeleteHelper
+ // a friend.
+ friend class base::DeleteHelper<BrowsingDataRemover>;
+ virtual ~BrowsingDataRemover();
+
+ // content::NotificationObserver method. Callback when TemplateURLService has
+ // finished loading. Deletes the entries from the model, and if we're not
+ // waiting on anything else notifies observers and deletes this
+ // BrowsingDataRemover.
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // WaitableEventWatcher implementation.
+ // Called when plug-in data has been cleared. Invokes NotifyAndDeleteIfDone.
+ virtual void OnWaitableEventSignaled(
+ base::WaitableEvent* waitable_event) OVERRIDE;
+
+ // PepperFlashSettingsManager::Client implementation.
+ virtual void OnDeauthorizeContentLicensesCompleted(uint32 request_id,
+ bool success) OVERRIDE;
+
+ // Removes the specified items related to browsing for a specific host. If the
+ // provided |origin| is empty, data is removed for all origins. The
+ // |origin_set_mask| parameter defines the set of origins from which data
+ // should be removed (protected, unprotected, or both).
+ void RemoveImpl(int remove_mask,
+ const GURL& origin,
+ int origin_set_mask);
+
+ // If we're not waiting on anything, notifies observers and deletes this
+ // object.
+ void NotifyAndDeleteIfDone();
+
+ // Callback when the network history has been deleted. Invokes
+ // NotifyAndDeleteIfDone.
+ void ClearedNetworkHistory();
+
+ // Invoked on the IO thread to clear the HostCache, speculative data about
+ // subresources on visited sites, and initial navigation history.
+ void ClearNetworkingHistory(IOThread* io_thread);
+
+ // Callback when the cache has been deleted. Invokes NotifyAndDeleteIfDone.
+ void ClearedCache();
+
+ // Invoked on the IO thread to delete from the cache.
+ void ClearCacheOnIOThread();
+
+ // Performs the actual work to delete the cache.
+ void DoClearCache(int rv);
+
+#if !defined(DISABLE_NACL)
+ // Callback for when the NaCl cache has been deleted. Invokes
+ // NotifyAndDeleteIfDone.
+ void ClearedNaClCache();
+
+ // Invokes the ClearedNaClCache on the UI thread.
+ void ClearedNaClCacheOnIOThread();
+
+ // Invoked on the IO thread to delete the NaCl cache.
+ void ClearNaClCacheOnIOThread();
+#endif
+
+ // Invoked on the UI thread to delete local storage.
+ void ClearLocalStorageOnUIThread();
+
+ // Callback to deal with the list gathered in ClearLocalStorageOnUIThread.
+ void OnGotLocalStorageUsageInfo(
+ const std::vector<dom_storage::DomStorageContext::UsageInfo>& infos);
+
+ // Callback on deletion of local storage data. Invokes NotifyAndDeleteIfDone.
+ void OnLocalStorageCleared();
+
+ // Invoked on the IO thread to delete all storage types managed by the quota
+ // system: AppCache, Databases, FileSystems.
+ void ClearQuotaManagedDataOnIOThread();
+
+ // Callback to respond to QuotaManager::GetOriginsModifiedSince, which is the
+ // core of 'ClearQuotaManagedDataOnIOThread'.
+ void OnGotQuotaManagedOrigins(const std::set<GURL>& origins,
+ quota::StorageType type);
+
+ // Callback responding to deletion of a single quota managed origin's
+ // persistent data
+ void OnQuotaManagedOriginDeletion(const GURL& origin,
+ quota::StorageType type,
+ quota::QuotaStatusCode);
+
+ // Called to check whether all temporary and persistent origin data that
+ // should be deleted has been deleted. If everything's good to go, invokes
+ // OnQuotaManagedDataDeleted on the UI thread.
+ void CheckQuotaManagedDataDeletionStatus();
+
+ // Completion handler that runs on the UI thread once persistent data has been
+ // deleted. Updates the waiting flag and invokes NotifyAndDeleteIfDone.
+ void OnQuotaManagedDataDeleted();
+
+ // Callback when Cookies has been deleted. Invokes NotifyAndDeleteIfDone.
+ void OnClearedCookies(int num_deleted);
+
+ // Invoked on the IO thread to delete cookies.
+ void ClearCookiesOnIOThread(net::URLRequestContextGetter* rq_context);
+
+ // Invoked on the IO thread to delete server bound certs.
+ void ClearServerBoundCertsOnIOThread(
+ net::URLRequestContextGetter* rq_context);
+
+ // Callback when server bound certs have been deleted. Invokes
+ // NotifyAndDeleteIfDone.
+ void OnClearedServerBoundCerts();
+
+ // Calculate the begin time for the deletion range specified by |time_period|.
+ base::Time CalculateBeginDeleteTime(TimePeriod time_period);
+
+ // Returns true if we're all done.
+ bool AllDone();
+
+ // Setter for removing_; DCHECKs that we can only start removing if we're not
+ // already removing, and vice-versa.
+ static void set_removing(bool removing);
+
+ content::NotificationRegistrar registrar_;
+
+ // Profile we're to remove from.
+ Profile* profile_;
+
+ // The QuotaManager is owned by the profile; we can use a raw pointer here,
+ // and rely on the profile to destroy the object whenever it's reasonable.
+ quota::QuotaManager* quota_manager_;
+
+ // The DOMStorageContext is owned by the profile; we'll store a raw pointer.
+ content::DOMStorageContext* dom_storage_context_;
+
+ // 'Protected' origins are not subject to data removal.
+ scoped_refptr<ExtensionSpecialStoragePolicy> special_storage_policy_;
+
+ // Start time to delete from.
+ const base::Time delete_begin_;
+
+ // End time to delete to.
+ const base::Time delete_end_;
+
+ // True if Remove has been invoked.
+ static bool removing_;
+
+ CacheState next_cache_state_;
+ disk_cache::Backend* cache_;
+
+ // Used to delete data from HTTP cache.
+ scoped_refptr<net::URLRequestContextGetter> main_context_getter_;
+ scoped_refptr<net::URLRequestContextGetter> media_context_getter_;
+
+ // Used to delete plugin data.
+ scoped_ptr<content::PluginDataRemover> plugin_data_remover_;
+ base::WaitableEventWatcher watcher_;
+
+ // Used to deauthorize content licenses for Pepper Flash.
+ scoped_ptr<PepperFlashSettingsManager> pepper_flash_settings_manager_;
+ uint32 deauthorize_content_licenses_request_id_;
+
+ // True if we're waiting for various data to be deleted.
+ // These may only be accessed from UI thread in order to avoid races!
+ bool waiting_for_clear_cache_;
+ bool waiting_for_clear_nacl_cache_;
+ // Non-zero if waiting for cookies to be cleared.
+ int waiting_for_clear_cookies_count_;
+ bool waiting_for_clear_history_;
+ bool waiting_for_clear_local_storage_;
+ bool waiting_for_clear_networking_history_;
+ bool waiting_for_clear_server_bound_certs_;
+ bool waiting_for_clear_plugin_data_;
+ bool waiting_for_clear_quota_managed_data_;
+ bool waiting_for_clear_content_licenses_;
+
+ // Tracking how many origins need to be deleted, and whether we're finished
+ // gathering origins.
+ int quota_managed_origins_to_delete_count_;
+ int quota_managed_storage_types_to_delete_count_;
+
+ // The removal mask for the current removal operation.
+ int remove_mask_;
+
+ // The origin for the current removal operation.
+ GURL remove_origin_;
+
+ // From which types of origins should we remove data?
+ int origin_set_mask_;
+
+ ObserverList<Observer> observer_list_;
+
+ // Used if we need to clear history.
+ CancelableRequestConsumer request_consumer_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataRemover);
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_REMOVER_H_
diff --git a/chrome/browser/browsing_data/browsing_data_remover_unittest.cc b/chrome/browser/browsing_data/browsing_data_remover_unittest.cc
new file mode 100644
index 0000000..594a6ab
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_remover_unittest.cc
@@ -0,0 +1,1156 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_remover.h"
+
+#include <set>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/platform_file.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/extensions/mock_extension_special_storage_policy.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_pref_service.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/dom_storage_context.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/test/test_browser_thread.h"
+#include "net/base/server_bound_cert_service.h"
+#include "net/base/server_bound_cert_store.h"
+#include "net/base/ssl_client_cert_type.h"
+#include "net/cookies/cookie_monster.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCString.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/quota/mock_quota_manager.h"
+#include "webkit/quota/quota_manager.h"
+#include "webkit/quota/quota_types.h"
+
+using content::BrowserThread;
+
+namespace {
+
+const char kTestOrigin1[] = "http://host1:1/";
+const char kTestOrigin2[] = "http://host2:1/";
+const char kTestOrigin3[] = "http://host3:1/";
+const char kTestOriginExt[] = "chrome-extension://abcdefghijklmnopqrstuvwxyz/";
+const char kTestOriginDevTools[] = "chrome-devtools://abcdefghijklmnopqrstuvw/";
+
+const GURL kOrigin1(kTestOrigin1);
+const GURL kOrigin2(kTestOrigin2);
+const GURL kOrigin3(kTestOrigin3);
+const GURL kOriginExt(kTestOriginExt);
+const GURL kOriginDevTools(kTestOriginDevTools);
+
+const FilePath::CharType kDomStorageOrigin1[] =
+ FILE_PATH_LITERAL("http_host1_1.localstorage");
+
+const FilePath::CharType kDomStorageOrigin2[] =
+ FILE_PATH_LITERAL("http_host2_1.localstorage");
+
+const FilePath::CharType kDomStorageOrigin3[] =
+ FILE_PATH_LITERAL("http_host3_1.localstorage");
+
+const FilePath::CharType kDomStorageExt[] = FILE_PATH_LITERAL(
+ "chrome-extension_abcdefghijklmnopqrstuvwxyz_0.localstorage");
+
+const quota::StorageType kTemporary = quota::kStorageTypeTemporary;
+const quota::StorageType kPersistent = quota::kStorageTypePersistent;
+
+const quota::QuotaClient::ID kClientFile = quota::QuotaClient::kFileSystem;
+const quota::QuotaClient::ID kClientDB = quota::QuotaClient::kIndexedDatabase;
+
+} // namespace
+
+class BrowsingDataRemoverTester : public BrowsingDataRemover::Observer {
+ public:
+ BrowsingDataRemoverTester()
+ : start_(false),
+ already_quit_(false) {}
+ virtual ~BrowsingDataRemoverTester() {}
+
+ void BlockUntilNotified() {
+ if (!already_quit_) {
+ DCHECK(!start_);
+ start_ = true;
+ MessageLoop::current()->Run();
+ } else {
+ DCHECK(!start_);
+ already_quit_ = false;
+ }
+ }
+
+ protected:
+ // BrowsingDataRemover::Observer implementation.
+ virtual void OnBrowsingDataRemoverDone() {
+ Notify();
+ }
+
+ void Notify() {
+ if (start_) {
+ DCHECK(!already_quit_);
+ MessageLoop::current()->Quit();
+ start_ = false;
+ } else {
+ DCHECK(!already_quit_);
+ already_quit_ = true;
+ }
+ }
+
+ private:
+ // Helps prevent from running message_loop, if the callback invoked
+ // immediately.
+ bool start_;
+ bool already_quit_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataRemoverTester);
+};
+
+// Testers -------------------------------------------------------------------
+
+class RemoveCookieTester : public BrowsingDataRemoverTester {
+ public:
+ RemoveCookieTester() : get_cookie_success_(false) {
+ }
+
+ // Returns true, if the given cookie exists in the cookie store.
+ bool ContainsCookie() {
+ get_cookie_success_ = false;
+ monster_->GetCookiesWithOptionsAsync(
+ kOrigin1, net::CookieOptions(),
+ base::Bind(&RemoveCookieTester::GetCookieCallback,
+ base::Unretained(this)));
+ BlockUntilNotified();
+ return get_cookie_success_;
+ }
+
+ void AddCookie() {
+ monster_->SetCookieWithOptionsAsync(
+ kOrigin1, "A=1", net::CookieOptions(),
+ base::Bind(&RemoveCookieTester::SetCookieCallback,
+ base::Unretained(this)));
+ BlockUntilNotified();
+ }
+
+ protected:
+ void SetMonster(net::CookieStore* monster) {
+ monster_ = monster;
+ }
+
+ private:
+ void GetCookieCallback(const std::string& cookies) {
+ if (cookies == "A=1") {
+ get_cookie_success_ = true;
+ } else {
+ EXPECT_EQ("", cookies);
+ get_cookie_success_ = false;
+ }
+ Notify();
+ }
+
+ void SetCookieCallback(bool result) {
+ ASSERT_TRUE(result);
+ Notify();
+ }
+
+ bool get_cookie_success_;
+
+ net::CookieStore* monster_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoveCookieTester);
+};
+
+class RemoveProfileCookieTester : public RemoveCookieTester {
+ public:
+ explicit RemoveProfileCookieTester(TestingProfile* profile) {
+ profile->CreateRequestContext();
+ SetMonster(profile->GetRequestContext()->GetURLRequestContext()->
+ cookie_store()->GetCookieMonster());
+ }
+};
+
+#if defined(ENABLE_SAFE_BROWSING)
+class RemoveSafeBrowsingCookieTester : public RemoveCookieTester {
+ public:
+ RemoveSafeBrowsingCookieTester()
+ : browser_process_(
+ static_cast<TestingBrowserProcess*>(g_browser_process)) {
+ scoped_refptr<SafeBrowsingService> sb_service =
+ SafeBrowsingService::CreateSafeBrowsingService();
+ browser_process_->SetSafeBrowsingService(sb_service);
+ sb_service->Initialize();
+ MessageLoop::current()->RunAllPending();
+
+ // Create a cookiemonster that does not have persistant storage, and replace
+ // the SafeBrowsingService created one with it.
+ net::CookieStore* monster = new net::CookieMonster(NULL, NULL);
+ sb_service->url_request_context()->GetURLRequestContext()->
+ set_cookie_store(monster);
+ SetMonster(monster);
+ }
+
+ virtual ~RemoveSafeBrowsingCookieTester() {
+ browser_process_->safe_browsing_service()->ShutDown();
+ MessageLoop::current()->RunAllPending();
+ browser_process_->SetSafeBrowsingService(NULL);
+ }
+
+ private:
+ TestingBrowserProcess* browser_process_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoveSafeBrowsingCookieTester);
+};
+#endif
+
+class RemoveServerBoundCertTester : public BrowsingDataRemoverTester {
+ public:
+ explicit RemoveServerBoundCertTester(TestingProfile* profile) {
+ profile->CreateRequestContext();
+ server_bound_cert_service_ = profile->GetRequestContext()->
+ GetURLRequestContext()->server_bound_cert_service();
+ }
+
+ int ServerBoundCertCount() {
+ return server_bound_cert_service_->cert_count();
+ }
+
+ // Add a server bound cert for |server| with specific creation and expiry
+ // times. The cert and key data will be filled with dummy values.
+ void AddServerBoundCertWithTimes(const std::string& server_identifier,
+ base::Time creation_time,
+ base::Time expiration_time) {
+ GetCertStore()->SetServerBoundCert(server_identifier,
+ net::CLIENT_CERT_RSA_SIGN, creation_time,
+ expiration_time, "a", "b");
+ }
+
+ // Add a server bound cert for |server|, with the current time as the
+ // creation time. The cert and key data will be filled with dummy values.
+ void AddServerBoundCert(const std::string& server_identifier) {
+ base::Time now = base::Time::Now();
+ AddServerBoundCertWithTimes(server_identifier,
+ now,
+ now + base::TimeDelta::FromDays(1));
+ }
+
+ net::ServerBoundCertStore* GetCertStore() {
+ return server_bound_cert_service_->GetCertStore();
+ }
+
+ private:
+ net::ServerBoundCertService* server_bound_cert_service_;
+
+ net::SSLClientCertType type_;
+ std::string key_;
+ std::string cert_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoveServerBoundCertTester);
+};
+
+class RemoveHistoryTester : public BrowsingDataRemoverTester {
+ public:
+ explicit RemoveHistoryTester(TestingProfile* profile)
+ : query_url_success_(false) {
+ profile->CreateHistoryService(true, false);
+ history_service_ = HistoryServiceFactory::GetForProfile(
+ profile, Profile::EXPLICIT_ACCESS);
+ }
+
+ // Returns true, if the given URL exists in the history service.
+ bool HistoryContainsURL(const GURL& url) {
+ history_service_->QueryURL(
+ url,
+ true,
+ &consumer_,
+ base::Bind(&RemoveHistoryTester::SaveResultAndQuit,
+ base::Unretained(this)));
+ BlockUntilNotified();
+ return query_url_success_;
+ }
+
+ void AddHistory(const GURL& url, base::Time time) {
+ history_service_->AddPage(url, time, NULL, 0, GURL(),
+ content::PAGE_TRANSITION_LINK, history::RedirectList(),
+ history::SOURCE_BROWSED, false);
+ }
+
+ private:
+ // Callback for HistoryService::QueryURL.
+ void SaveResultAndQuit(HistoryService::Handle,
+ bool success,
+ const history::URLRow*,
+ history::VisitVector*) {
+ query_url_success_ = success;
+ Notify();
+ }
+
+
+ // For History requests.
+ CancelableRequestConsumer consumer_;
+ bool query_url_success_;
+
+ // TestingProfile owns the history service; we shouldn't delete it.
+ HistoryService* history_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoveHistoryTester);
+};
+
+class RemoveLocalStorageTester : public BrowsingDataRemoverTester {
+ public:
+ explicit RemoveLocalStorageTester(TestingProfile* profile)
+ : profile_(profile), dom_storage_context_(NULL) {
+ dom_storage_context_ =
+ content::BrowserContext::GetDefaultDOMStorageContext(profile);
+ }
+
+ // Returns true, if the given origin URL exists.
+ bool DOMStorageExistsForOrigin(const GURL& origin) {
+ GetUsageInfo();
+ BlockUntilNotified();
+ for (size_t i = 0; i < infos_.size(); ++i) {
+ if (origin == infos_[i].origin)
+ return true;
+ }
+ return false;
+ }
+
+ void AddDOMStorageTestData() {
+ // Note: This test depends on details of how the dom_storage library
+ // stores data in the host file system.
+ FilePath storage_path = profile_->GetPath().AppendASCII("Local Storage");
+ file_util::CreateDirectory(storage_path);
+
+ // Write some files.
+ file_util::WriteFile(storage_path.Append(kDomStorageOrigin1), NULL, 0);
+ file_util::WriteFile(storage_path.Append(kDomStorageOrigin2), NULL, 0);
+ file_util::WriteFile(storage_path.Append(kDomStorageOrigin3), NULL, 0);
+ file_util::WriteFile(storage_path.Append(kDomStorageExt), NULL, 0);
+
+ // Tweak their dates.
+ file_util::SetLastModifiedTime(storage_path.Append(kDomStorageOrigin1),
+ base::Time::Now());
+ file_util::SetLastModifiedTime(storage_path.Append(kDomStorageOrigin2),
+ base::Time::Now() - base::TimeDelta::FromDays(1));
+ file_util::SetLastModifiedTime(storage_path.Append(kDomStorageOrigin3),
+ base::Time::Now() - base::TimeDelta::FromDays(60));
+ file_util::SetLastModifiedTime(storage_path.Append(kDomStorageExt),
+ base::Time::Now());
+ }
+
+ private:
+ void GetUsageInfo() {
+ dom_storage_context_->GetUsageInfo(
+ base::Bind(&RemoveLocalStorageTester::OnGotUsageInfo,
+ base::Unretained(this)));
+ }
+ void OnGotUsageInfo(
+ const std::vector<dom_storage::DomStorageContext::UsageInfo>& infos) {
+ infos_ = infos;
+ Notify();
+ }
+
+ // We don't own these pointers.
+ TestingProfile* profile_;
+ content::DOMStorageContext* dom_storage_context_;
+
+ std::vector<dom_storage::DomStorageContext::UsageInfo> infos_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoveLocalStorageTester);
+};
+class RemoveQuotaManagedDataTester : public BrowsingDataRemoverTester {
+ public:
+ RemoveQuotaManagedDataTester() {}
+ virtual ~RemoveQuotaManagedDataTester() {}
+
+ void PopulateTestQuotaManagedData(quota::MockQuotaManager* manager) {
+ // Set up kOrigin1 with a temporary quota, kOrigin2 with a persistent
+ // quota, and kOrigin3 with both. kOrigin1 is modified now, kOrigin2
+ // is modified at the beginning of time, and kOrigin3 is modified one day
+ // ago.
+ PopulateTestQuotaManagedPersistentData(manager);
+ PopulateTestQuotaManagedTemporaryData(manager);
+ }
+
+ void PopulateTestQuotaManagedNonBrowsingData(
+ quota::MockQuotaManager* manager) {
+ manager->AddOrigin(kOriginDevTools, kTemporary, kClientFile, base::Time());
+ manager->AddOrigin(kOriginDevTools, kPersistent, kClientFile, base::Time());
+ manager->AddOrigin(kOriginExt, kTemporary, kClientFile, base::Time());
+ manager->AddOrigin(kOriginExt, kPersistent, kClientFile, base::Time());
+ }
+
+ void PopulateTestQuotaManagedPersistentData(
+ quota::MockQuotaManager* manager) {
+ manager->AddOrigin(kOrigin2, kPersistent, kClientFile, base::Time());
+ manager->AddOrigin(kOrigin3, kPersistent, kClientFile,
+ base::Time::Now() - base::TimeDelta::FromDays(1));
+
+ EXPECT_FALSE(manager->OriginHasData(kOrigin1, kPersistent, kClientFile));
+ EXPECT_TRUE(manager->OriginHasData(kOrigin2, kPersistent, kClientFile));
+ EXPECT_TRUE(manager->OriginHasData(kOrigin3, kPersistent, kClientFile));
+ }
+
+ void PopulateTestQuotaManagedTemporaryData(quota::MockQuotaManager* manager) {
+ manager->AddOrigin(kOrigin1, kTemporary, kClientFile, base::Time::Now());
+ manager->AddOrigin(kOrigin3, kTemporary, kClientFile,
+ base::Time::Now() - base::TimeDelta::FromDays(1));
+
+ EXPECT_TRUE(manager->OriginHasData(kOrigin1, kTemporary, kClientFile));
+ EXPECT_FALSE(manager->OriginHasData(kOrigin2, kTemporary, kClientFile));
+ EXPECT_TRUE(manager->OriginHasData(kOrigin3, kTemporary, kClientFile));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RemoveQuotaManagedDataTester);
+};
+
+// Test Class ----------------------------------------------------------------
+
+class BrowsingDataRemoverTest : public testing::Test,
+ public content::NotificationObserver {
+ public:
+ BrowsingDataRemoverTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_),
+ db_thread_(BrowserThread::DB, &message_loop_),
+ webkit_thread_(BrowserThread::WEBKIT_DEPRECATED, &message_loop_),
+ file_thread_(BrowserThread::FILE, &message_loop_),
+ file_user_blocking_thread_(
+ BrowserThread::FILE_USER_BLOCKING, &message_loop_),
+ io_thread_(BrowserThread::IO, &message_loop_),
+ profile_(new TestingProfile()) {
+ registrar_.Add(this, chrome::NOTIFICATION_BROWSING_DATA_REMOVED,
+ content::Source<Profile>(profile_.get()));
+ }
+
+ virtual ~BrowsingDataRemoverTest() {
+ }
+
+ void TearDown() {
+ // TestingProfile contains a DOMStorageContext. BrowserContext's destructor
+ // posts a message to the WEBKIT thread to delete some of its member
+ // variables. We need to ensure that the profile is destroyed, and that
+ // the message loop is cleared out, before destroying the threads and loop.
+ // Otherwise we leak memory.
+ profile_.reset();
+ message_loop_.RunAllPending();
+ }
+
+ void BlockUntilBrowsingDataRemoved(BrowsingDataRemover::TimePeriod period,
+ int remove_mask,
+ bool include_protected_origins,
+ BrowsingDataRemoverTester* tester) {
+ BrowsingDataRemover* remover = new BrowsingDataRemover(
+ profile_.get(), period,
+ base::Time::Now() + base::TimeDelta::FromMilliseconds(10));
+ remover->OverrideQuotaManagerForTesting(GetMockManager());
+ remover->AddObserver(tester);
+
+ called_with_details_.reset(new BrowsingDataRemover::NotificationDetails());
+
+ // BrowsingDataRemover deletes itself when it completes.
+ int origin_set_mask = BrowsingDataHelper::UNPROTECTED_WEB;
+ if (include_protected_origins)
+ origin_set_mask |= BrowsingDataHelper::PROTECTED_WEB;
+ remover->Remove(remove_mask, origin_set_mask);
+ tester->BlockUntilNotified();
+ }
+
+ void BlockUntilOriginDataRemoved(BrowsingDataRemover::TimePeriod period,
+ int remove_mask,
+ const GURL& remove_origin,
+ BrowsingDataRemoverTester* tester) {
+ BrowsingDataRemover* remover = new BrowsingDataRemover(
+ profile_.get(), period,
+ base::Time::Now() + base::TimeDelta::FromMilliseconds(10));
+ remover->OverrideQuotaManagerForTesting(GetMockManager());
+ remover->AddObserver(tester);
+
+ called_with_details_.reset(new BrowsingDataRemover::NotificationDetails());
+
+ // BrowsingDataRemover deletes itself when it completes.
+ remover->RemoveImpl(remove_mask, remove_origin,
+ BrowsingDataHelper::UNPROTECTED_WEB);
+ tester->BlockUntilNotified();
+ }
+
+ TestingProfile* GetProfile() {
+ return profile_.get();
+ }
+
+ base::Time GetBeginTime() {
+ return called_with_details_->removal_begin;
+ }
+
+ int GetRemovalMask() {
+ return called_with_details_->removal_mask;
+ }
+
+ int GetOriginSetMask() {
+ return called_with_details_->origin_set_mask;
+ }
+
+ quota::MockQuotaManager* GetMockManager() {
+ if (!quota_manager_) {
+ quota_manager_ = new quota::MockQuotaManager(
+ profile_->IsOffTheRecord(),
+ profile_->GetPath(),
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB),
+ profile_->GetExtensionSpecialStoragePolicy());
+ }
+ return quota_manager_;
+ }
+
+ // content::NotificationObserver implementation.
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE {
+ DCHECK_EQ(type, chrome::NOTIFICATION_BROWSING_DATA_REMOVED);
+
+ // We're not taking ownership of the details object, but storing a copy of
+ // it locally.
+ called_with_details_.reset(new BrowsingDataRemover::NotificationDetails(
+ *content::Details<BrowsingDataRemover::NotificationDetails>(
+ details).ptr()));
+
+ registrar_.RemoveAll();
+ }
+
+ protected:
+ RemoveQuotaManagedDataTester tester_;
+
+ private:
+ scoped_ptr<BrowsingDataRemover::NotificationDetails> called_with_details_;
+ content::NotificationRegistrar registrar_;
+
+ // message_loop_, as well as all the threads associated with it must be
+ // defined before profile_ to prevent explosions. Oh how I love C++.
+ MessageLoopForUI message_loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread db_thread_;
+ content::TestBrowserThread webkit_thread_;
+ content::TestBrowserThread file_thread_;
+ content::TestBrowserThread file_user_blocking_thread_;
+ content::TestBrowserThread io_thread_;
+ scoped_ptr<TestingProfile> profile_;
+ scoped_refptr<quota::MockQuotaManager> quota_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataRemoverTest);
+};
+
+// Tests ---------------------------------------------------------------------
+
+TEST_F(BrowsingDataRemoverTest, RemoveCookieForever) {
+ scoped_ptr<RemoveProfileCookieTester> tester(
+ new RemoveProfileCookieTester(GetProfile()));
+
+ tester->AddCookie();
+ ASSERT_TRUE(tester->ContainsCookie());
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_COOKIES, false, tester.get());
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_COOKIES, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(tester->ContainsCookie());
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveCookieLastHour) {
+ scoped_ptr<RemoveProfileCookieTester> tester(
+ new RemoveProfileCookieTester(GetProfile()));
+
+ tester->AddCookie();
+ ASSERT_TRUE(tester->ContainsCookie());
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::LAST_HOUR,
+ BrowsingDataRemover::REMOVE_COOKIES, false, tester.get());
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_COOKIES, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(tester->ContainsCookie());
+}
+
+#if defined(ENABLE_SAFE_BROWSING)
+TEST_F(BrowsingDataRemoverTest, RemoveSafeBrowsingCookieForever) {
+ scoped_ptr<RemoveSafeBrowsingCookieTester> tester(
+ new RemoveSafeBrowsingCookieTester());
+
+ tester->AddCookie();
+ ASSERT_TRUE(tester->ContainsCookie());
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_COOKIES, false, tester.get());
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_COOKIES, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(tester->ContainsCookie());
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveSafeBrowsingCookieLastHour) {
+ scoped_ptr<RemoveSafeBrowsingCookieTester> tester(
+ new RemoveSafeBrowsingCookieTester());
+
+ tester->AddCookie();
+ ASSERT_TRUE(tester->ContainsCookie());
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::LAST_HOUR,
+ BrowsingDataRemover::REMOVE_COOKIES, false, tester.get());
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_COOKIES, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ // Removing with time period other than EVERYTHING should not clear safe
+ // browsing cookies.
+ EXPECT_TRUE(tester->ContainsCookie());
+}
+#endif
+
+TEST_F(BrowsingDataRemoverTest, RemoveServerBoundCertForever) {
+ scoped_ptr<RemoveServerBoundCertTester> tester(
+ new RemoveServerBoundCertTester(GetProfile()));
+
+ tester->AddServerBoundCert(kTestOrigin1);
+ EXPECT_EQ(1, tester->ServerBoundCertCount());
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_SERVER_BOUND_CERTS, false, tester.get());
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_SERVER_BOUND_CERTS, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_EQ(0, tester->ServerBoundCertCount());
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveServerBoundCertLastHour) {
+ scoped_ptr<RemoveServerBoundCertTester> tester(
+ new RemoveServerBoundCertTester(GetProfile()));
+
+ base::Time now = base::Time::Now();
+ tester->AddServerBoundCert(kTestOrigin1);
+ tester->AddServerBoundCertWithTimes(kTestOrigin2,
+ now - base::TimeDelta::FromHours(2),
+ now);
+ EXPECT_EQ(2, tester->ServerBoundCertCount());
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::LAST_HOUR,
+ BrowsingDataRemover::REMOVE_SERVER_BOUND_CERTS, false, tester.get());
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_SERVER_BOUND_CERTS, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_EQ(1, tester->ServerBoundCertCount());
+ net::ServerBoundCertStore::ServerBoundCertList certs;
+ tester->GetCertStore()->GetAllServerBoundCerts(&certs);
+ EXPECT_EQ(kTestOrigin2, certs.front().server_identifier());
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveUnprotectedLocalStorageForever) {
+ // Protect kOrigin1.
+ scoped_refptr<MockExtensionSpecialStoragePolicy> mock_policy =
+ new MockExtensionSpecialStoragePolicy;
+ mock_policy->AddProtected(kOrigin1.GetOrigin());
+ GetProfile()->SetExtensionSpecialStoragePolicy(mock_policy);
+
+ scoped_ptr<RemoveLocalStorageTester> tester(
+ new RemoveLocalStorageTester(GetProfile()));
+
+ tester->AddDOMStorageTestData();
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOrigin1));
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOrigin2));
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOrigin3));
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOriginExt));
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_LOCAL_STORAGE, false, &tester_);
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_LOCAL_STORAGE, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOrigin1));
+ EXPECT_FALSE(tester->DOMStorageExistsForOrigin(kOrigin2));
+ EXPECT_FALSE(tester->DOMStorageExistsForOrigin(kOrigin3));
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOriginExt));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveProtectedLocalStorageForever) {
+ // Protect kOrigin1.
+ scoped_refptr<MockExtensionSpecialStoragePolicy> mock_policy =
+ new MockExtensionSpecialStoragePolicy;
+ mock_policy->AddProtected(kOrigin1.GetOrigin());
+ GetProfile()->SetExtensionSpecialStoragePolicy(mock_policy);
+
+ scoped_ptr<RemoveLocalStorageTester> tester(
+ new RemoveLocalStorageTester(GetProfile()));
+
+ tester->AddDOMStorageTestData();
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOrigin1));
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOrigin2));
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOrigin3));
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOriginExt));
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_LOCAL_STORAGE, true, &tester_);
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_LOCAL_STORAGE, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::PROTECTED_WEB |
+ BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(tester->DOMStorageExistsForOrigin(kOrigin1));
+ EXPECT_FALSE(tester->DOMStorageExistsForOrigin(kOrigin2));
+ EXPECT_FALSE(tester->DOMStorageExistsForOrigin(kOrigin3));
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOriginExt));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveLocalStorageForLastWeek) {
+ scoped_ptr<RemoveLocalStorageTester> tester(
+ new RemoveLocalStorageTester(GetProfile()));
+
+ tester->AddDOMStorageTestData();
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOrigin1));
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOrigin2));
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOrigin3));
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::LAST_WEEK,
+ BrowsingDataRemover::REMOVE_LOCAL_STORAGE, false, &tester_);
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_LOCAL_STORAGE, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(tester->DOMStorageExistsForOrigin(kOrigin1));
+ EXPECT_FALSE(tester->DOMStorageExistsForOrigin(kOrigin2));
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOrigin3));
+ EXPECT_TRUE(tester->DOMStorageExistsForOrigin(kOriginExt));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveHistoryForever) {
+ scoped_ptr<RemoveHistoryTester> tester(
+ new RemoveHistoryTester(GetProfile()));
+
+ tester->AddHistory(kOrigin1, base::Time::Now());
+ ASSERT_TRUE(tester->HistoryContainsURL(kOrigin1));
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_HISTORY, false, tester.get());
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_HISTORY, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(tester->HistoryContainsURL(kOrigin1));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveHistoryForLastHour) {
+ scoped_ptr<RemoveHistoryTester> tester(
+ new RemoveHistoryTester(GetProfile()));
+
+ base::Time two_hours_ago = base::Time::Now() - base::TimeDelta::FromHours(2);
+
+ tester->AddHistory(kOrigin1, base::Time::Now());
+ tester->AddHistory(kOrigin2, two_hours_ago);
+ ASSERT_TRUE(tester->HistoryContainsURL(kOrigin1));
+ ASSERT_TRUE(tester->HistoryContainsURL(kOrigin2));
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::LAST_HOUR,
+ BrowsingDataRemover::REMOVE_HISTORY, false, tester.get());
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_HISTORY, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(tester->HistoryContainsURL(kOrigin1));
+ EXPECT_TRUE(tester->HistoryContainsURL(kOrigin2));
+}
+
+TEST_F(BrowsingDataRemoverTest, QuotaClientMaskGeneration) {
+ EXPECT_EQ(quota::QuotaClient::kFileSystem,
+ BrowsingDataRemover::GenerateQuotaClientMask(
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS));
+ EXPECT_EQ(quota::QuotaClient::kDatabase,
+ BrowsingDataRemover::GenerateQuotaClientMask(
+ BrowsingDataRemover::REMOVE_WEBSQL));
+ EXPECT_EQ(quota::QuotaClient::kAppcache,
+ BrowsingDataRemover::GenerateQuotaClientMask(
+ BrowsingDataRemover::REMOVE_APPCACHE));
+ EXPECT_EQ(quota::QuotaClient::kIndexedDatabase,
+ BrowsingDataRemover::GenerateQuotaClientMask(
+ BrowsingDataRemover::REMOVE_INDEXEDDB));
+ EXPECT_EQ(quota::QuotaClient::kFileSystem |
+ quota::QuotaClient::kDatabase |
+ quota::QuotaClient::kAppcache |
+ quota::QuotaClient::kIndexedDatabase,
+ BrowsingDataRemover::GenerateQuotaClientMask(
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedDataForeverBoth) {
+ tester_.PopulateTestQuotaManagedData(GetMockManager());
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB, false, &tester_);
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin3, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kPersistent,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kPersistent,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin3, kPersistent,
+ kClientFile));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedDataForeverOnlyTemporary) {
+ tester_.PopulateTestQuotaManagedTemporaryData(GetMockManager());
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB, false, &tester_);
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin3, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kPersistent,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kPersistent,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin3, kPersistent,
+ kClientFile));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedDataForeverOnlyPersistent) {
+ tester_.PopulateTestQuotaManagedPersistentData(GetMockManager());
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB, false, &tester_);
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin3, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kPersistent,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kPersistent,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin3, kPersistent,
+ kClientFile));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedDataForeverNeither) {
+ GetMockManager(); // Creates the QuotaManager instance.
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB, false, &tester_);
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin3, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kPersistent,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kPersistent,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin3, kPersistent,
+ kClientFile));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedDataForeverSpecificOrigin) {
+ tester_.PopulateTestQuotaManagedData(GetMockManager());
+
+ // Remove Origin 1.
+ BlockUntilOriginDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_INDEXEDDB |
+ BrowsingDataRemover::REMOVE_WEBSQL, kOrigin1, &tester_);
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_INDEXEDDB |
+ BrowsingDataRemover::REMOVE_WEBSQL, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kTemporary,
+ kClientFile));
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin3, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kPersistent,
+ kClientFile));
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin2, kPersistent,
+ kClientFile));
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin3, kPersistent,
+ kClientFile));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedDataForLastHour) {
+ tester_.PopulateTestQuotaManagedData(GetMockManager());
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::LAST_HOUR,
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB, false, &tester_);
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kTemporary,
+ kClientFile));
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin3, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kPersistent,
+ kClientFile));
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin2, kPersistent,
+ kClientFile));
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin3, kPersistent,
+ kClientFile));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedDataForLastWeek) {
+ tester_.PopulateTestQuotaManagedData(GetMockManager());
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::LAST_WEEK,
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB, false, &tester_);
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin3, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kPersistent,
+ kClientFile));
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin2, kPersistent,
+ kClientFile));
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin3, kPersistent,
+ kClientFile));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedUnprotectedOrigins) {
+ // Protect kOrigin1.
+ scoped_refptr<MockExtensionSpecialStoragePolicy> mock_policy =
+ new MockExtensionSpecialStoragePolicy;
+ mock_policy->AddProtected(kOrigin1.GetOrigin());
+ GetProfile()->SetExtensionSpecialStoragePolicy(mock_policy);
+
+ tester_.PopulateTestQuotaManagedData(GetMockManager());
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB, false, &tester_);
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_WEBSQL |
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_INDEXEDDB, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin3, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kPersistent,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kPersistent,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin3, kPersistent,
+ kClientFile));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedProtectedSpecificOrigin) {
+ // Protect kOrigin1.
+ scoped_refptr<MockExtensionSpecialStoragePolicy> mock_policy =
+ new MockExtensionSpecialStoragePolicy;
+ mock_policy->AddProtected(kOrigin1.GetOrigin());
+ GetProfile()->SetExtensionSpecialStoragePolicy(mock_policy);
+
+ tester_.PopulateTestQuotaManagedData(GetMockManager());
+
+ // Try to remove kOrigin1. Expect failure.
+ BlockUntilOriginDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_INDEXEDDB |
+ BrowsingDataRemover::REMOVE_WEBSQL, kOrigin1, &tester_);
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_INDEXEDDB |
+ BrowsingDataRemover::REMOVE_WEBSQL, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kTemporary,
+ kClientFile));
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin3, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kPersistent,
+ kClientFile));
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin2, kPersistent,
+ kClientFile));
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOrigin3, kPersistent,
+ kClientFile));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedProtectedOrigins) {
+ // Protect kOrigin1.
+ scoped_refptr<MockExtensionSpecialStoragePolicy> mock_policy =
+ new MockExtensionSpecialStoragePolicy;
+ mock_policy->AddProtected(kOrigin1.GetOrigin());
+ GetProfile()->SetExtensionSpecialStoragePolicy(mock_policy);
+
+ tester_.PopulateTestQuotaManagedData(GetMockManager());
+
+ // Try to remove kOrigin1. Expect success.
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_INDEXEDDB |
+ BrowsingDataRemover::REMOVE_WEBSQL, true, &tester_);
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_INDEXEDDB |
+ BrowsingDataRemover::REMOVE_WEBSQL, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::PROTECTED_WEB |
+ BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin3, kTemporary,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin1, kPersistent,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin2, kPersistent,
+ kClientFile));
+ EXPECT_FALSE(GetMockManager()->OriginHasData(kOrigin3, kPersistent,
+ kClientFile));
+}
+
+TEST_F(BrowsingDataRemoverTest, RemoveQuotaManagedIgnoreExtensionsAndDevTools) {
+ tester_.PopulateTestQuotaManagedNonBrowsingData(GetMockManager());
+
+ BlockUntilBrowsingDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_INDEXEDDB |
+ BrowsingDataRemover::REMOVE_WEBSQL, false, &tester_);
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_APPCACHE |
+ BrowsingDataRemover::REMOVE_FILE_SYSTEMS |
+ BrowsingDataRemover::REMOVE_INDEXEDDB |
+ BrowsingDataRemover::REMOVE_WEBSQL, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+
+ // Check that extension and devtools data isn't removed.
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOriginExt, kTemporary,
+ kClientFile));
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOriginExt, kPersistent,
+ kClientFile));
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOriginDevTools, kTemporary,
+ kClientFile));
+ EXPECT_TRUE(GetMockManager()->OriginHasData(kOriginDevTools, kPersistent,
+ kClientFile));
+}
+
+TEST_F(BrowsingDataRemoverTest, OriginBasedHistoryRemoval) {
+ scoped_ptr<RemoveHistoryTester> tester(
+ new RemoveHistoryTester(GetProfile()));
+
+ base::Time two_hours_ago = base::Time::Now() - base::TimeDelta::FromHours(2);
+
+ tester->AddHistory(kOrigin1, base::Time::Now());
+ tester->AddHistory(kOrigin2, two_hours_ago);
+ ASSERT_TRUE(tester->HistoryContainsURL(kOrigin1));
+ ASSERT_TRUE(tester->HistoryContainsURL(kOrigin2));
+
+ BlockUntilOriginDataRemoved(BrowsingDataRemover::EVERYTHING,
+ BrowsingDataRemover::REMOVE_HISTORY, kOrigin2, tester.get());
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_HISTORY, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_TRUE(tester->HistoryContainsURL(kOrigin1));
+ EXPECT_FALSE(tester->HistoryContainsURL(kOrigin2));
+}
+
+TEST_F(BrowsingDataRemoverTest, OriginAndTimeBasedHistoryRemoval) {
+ scoped_ptr<RemoveHistoryTester> tester(
+ new RemoveHistoryTester(GetProfile()));
+
+ base::Time two_hours_ago = base::Time::Now() - base::TimeDelta::FromHours(2);
+
+ tester->AddHistory(kOrigin1, base::Time::Now());
+ tester->AddHistory(kOrigin2, two_hours_ago);
+ ASSERT_TRUE(tester->HistoryContainsURL(kOrigin1));
+ ASSERT_TRUE(tester->HistoryContainsURL(kOrigin2));
+
+ BlockUntilOriginDataRemoved(BrowsingDataRemover::LAST_HOUR,
+ BrowsingDataRemover::REMOVE_HISTORY, kOrigin2, tester.get());
+
+ EXPECT_EQ(BrowsingDataRemover::REMOVE_HISTORY, GetRemovalMask());
+ EXPECT_EQ(BrowsingDataHelper::UNPROTECTED_WEB, GetOriginSetMask());
+ EXPECT_TRUE(tester->HistoryContainsURL(kOrigin1));
+ EXPECT_TRUE(tester->HistoryContainsURL(kOrigin2));
+}
diff --git a/chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.cc b/chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.cc
new file mode 100644
index 0000000..2b0111a
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/server_bound_cert_service.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace {
+
+class BrowsingDataServerBoundCertHelperImpl
+ : public BrowsingDataServerBoundCertHelper {
+ public:
+ explicit BrowsingDataServerBoundCertHelperImpl(Profile* profile);
+
+ // BrowsingDataServerBoundCertHelper methods.
+ virtual void StartFetching(const FetchResultCallback& callback) OVERRIDE;
+ virtual void DeleteServerBoundCert(const std::string& server_id) OVERRIDE;
+
+ private:
+ virtual ~BrowsingDataServerBoundCertHelperImpl();
+
+ // Fetch the certs. This must be called in the IO thread.
+ void FetchOnIOThread();
+
+ // Notifies the completion callback. This must be called in the UI thread.
+ void NotifyInUIThread();
+
+ // Delete a single cert. This must be called in IO thread.
+ void DeleteOnIOThread(const std::string& server_id);
+
+ // Access to |server_bound_cert_list_| is triggered indirectly via the UI
+ // thread and guarded by |is_fetching_|. This means |server_bound_cert_list_|
+ // is only accessed while |is_fetching_| is true. The flag |is_fetching_| is
+ // only accessed on the UI thread.
+ net::ServerBoundCertStore::ServerBoundCertList server_bound_cert_list_;
+
+ // Indicates whether or not we're currently fetching information:
+ // it's true when StartFetching() is called in the UI thread, and it's reset
+ // after we notify the callback in the UI thread.
+ // This only mutates on the UI thread.
+ bool is_fetching_;
+
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+
+ // This only mutates on the UI thread.
+ FetchResultCallback completion_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingDataServerBoundCertHelperImpl);
+};
+
+BrowsingDataServerBoundCertHelperImpl::
+BrowsingDataServerBoundCertHelperImpl(Profile* profile)
+ : is_fetching_(false),
+ request_context_getter_(profile->GetRequestContext()) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+}
+
+BrowsingDataServerBoundCertHelperImpl::
+~BrowsingDataServerBoundCertHelperImpl() {
+}
+
+void BrowsingDataServerBoundCertHelperImpl::StartFetching(
+ const FetchResultCallback& callback) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(!is_fetching_);
+ DCHECK(!callback.is_null());
+ DCHECK(completion_callback_.is_null());
+ is_fetching_ = true;
+ completion_callback_ = callback;
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrowsingDataServerBoundCertHelperImpl::FetchOnIOThread,
+ this));
+}
+
+void BrowsingDataServerBoundCertHelperImpl::DeleteServerBoundCert(
+ const std::string& server_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO, FROM_HERE,
+ base::Bind(&BrowsingDataServerBoundCertHelperImpl::DeleteOnIOThread,
+ this, server_id));
+}
+
+void BrowsingDataServerBoundCertHelperImpl::FetchOnIOThread() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ net::ServerBoundCertStore* cert_store =
+ request_context_getter_->GetURLRequestContext()->
+ server_bound_cert_service()->GetCertStore();
+ if (cert_store) {
+ server_bound_cert_list_.clear();
+ cert_store->GetAllServerBoundCerts(&server_bound_cert_list_);
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::Bind(&BrowsingDataServerBoundCertHelperImpl::NotifyInUIThread,
+ this));
+ }
+}
+
+void BrowsingDataServerBoundCertHelperImpl::NotifyInUIThread() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(is_fetching_);
+ is_fetching_ = false;
+ completion_callback_.Run(server_bound_cert_list_);
+ completion_callback_.Reset();
+}
+
+void BrowsingDataServerBoundCertHelperImpl::DeleteOnIOThread(
+ const std::string& server_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ net::ServerBoundCertStore* cert_store =
+ request_context_getter_->GetURLRequestContext()->
+ server_bound_cert_service()->GetCertStore();
+ if (cert_store)
+ cert_store->DeleteServerBoundCert(server_id);
+}
+
+} // namespace
+
+// static
+BrowsingDataServerBoundCertHelper*
+BrowsingDataServerBoundCertHelper::Create(Profile* profile) {
+ return new BrowsingDataServerBoundCertHelperImpl(profile);
+}
+
+CannedBrowsingDataServerBoundCertHelper::
+CannedBrowsingDataServerBoundCertHelper() {}
+
+CannedBrowsingDataServerBoundCertHelper::
+~CannedBrowsingDataServerBoundCertHelper() {}
+
+CannedBrowsingDataServerBoundCertHelper*
+CannedBrowsingDataServerBoundCertHelper::Clone() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ CannedBrowsingDataServerBoundCertHelper* clone =
+ new CannedBrowsingDataServerBoundCertHelper();
+
+ clone->server_bound_cert_map_ = server_bound_cert_map_;
+ return clone;
+}
+
+void CannedBrowsingDataServerBoundCertHelper::AddServerBoundCert(
+ const net::ServerBoundCertStore::ServerBoundCert& server_bound_cert) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ server_bound_cert_map_[server_bound_cert.server_identifier()] =
+ server_bound_cert;
+}
+
+void CannedBrowsingDataServerBoundCertHelper::Reset() {
+ server_bound_cert_map_.clear();
+}
+
+bool CannedBrowsingDataServerBoundCertHelper::empty() const {
+ return server_bound_cert_map_.empty();
+}
+
+size_t CannedBrowsingDataServerBoundCertHelper::GetCertCount() const {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ return server_bound_cert_map_.size();
+}
+
+void CannedBrowsingDataServerBoundCertHelper::StartFetching(
+ const FetchResultCallback& callback) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ if (callback.is_null())
+ return;
+ // We post a task to emulate async fetching behavior.
+ completion_callback_ = callback;
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&CannedBrowsingDataServerBoundCertHelper::FinishFetching,
+ this));
+}
+
+void CannedBrowsingDataServerBoundCertHelper::FinishFetching() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ net::ServerBoundCertStore::ServerBoundCertList cert_list;
+ for (ServerBoundCertMap::iterator i = server_bound_cert_map_.begin();
+ i != server_bound_cert_map_.end(); ++i)
+ cert_list.push_back(i->second);
+ completion_callback_.Run(cert_list);
+}
+
+void CannedBrowsingDataServerBoundCertHelper::DeleteServerBoundCert(
+ const std::string& server_id) {
+ NOTREACHED();
+}
diff --git a/chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.h b/chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.h
new file mode 100644
index 0000000..a90c3da
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_SERVER_BOUND_CERT_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_SERVER_BOUND_CERT_HELPER_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "net/base/server_bound_cert_store.h"
+
+class Profile;
+
+// BrowsingDataServerBoundCertHelper is an interface for classes dealing with
+// aggregating and deleting browsing data stored in the server bound cert store.
+// A client of this class need to call StartFetching from the UI thread to
+// initiate the flow, and it'll be notified by the callback in its UI thread at
+// some later point.
+class BrowsingDataServerBoundCertHelper
+ : public base::RefCountedThreadSafe<BrowsingDataServerBoundCertHelper> {
+ public:
+
+ // Create a BrowsingDataServerBoundCertHelper instance for the given
+ // |profile|.
+ static BrowsingDataServerBoundCertHelper* Create(Profile* profile);
+
+ typedef base::Callback<
+ void(const net::ServerBoundCertStore::ServerBoundCertList&)>
+ FetchResultCallback;
+
+ // Starts the fetching process, which will notify its completion via
+ // callback.
+ // This must be called only in the UI thread.
+ virtual void StartFetching(const FetchResultCallback& callback) = 0;
+ // Requests a single server bound cert to be deleted. This must be called in
+ // the UI thread.
+ virtual void DeleteServerBoundCert(const std::string& server_id) = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<BrowsingDataServerBoundCertHelper>;
+ virtual ~BrowsingDataServerBoundCertHelper() {}
+};
+
+// This class is a thin wrapper around BrowsingDataServerBoundCertHelper that
+// does not fetch its information from the ServerBoundCertService, but gets them
+// passed as a parameter during construction.
+class CannedBrowsingDataServerBoundCertHelper
+ : public BrowsingDataServerBoundCertHelper {
+ public:
+ CannedBrowsingDataServerBoundCertHelper();
+
+ // Return a copy of the ServerBoundCert helper. Only one consumer can use the
+ // StartFetching method at a time, so we need to create a copy of the helper
+ // every time we instantiate a cookies tree model for it.
+ CannedBrowsingDataServerBoundCertHelper* Clone();
+
+ // Add an ServerBoundCert to the set of canned server bound certs that is
+ // returned by this helper.
+ void AddServerBoundCert(
+ const net::ServerBoundCertStore::ServerBoundCert& server_bound_cert);
+
+ // Clears the list of canned server bound certs.
+ void Reset();
+
+ // True if no ServerBoundCerts are currently stored.
+ bool empty() const;
+
+ // Returns the current number of server bound certificates.
+ size_t GetCertCount() const;
+
+ // BrowsingDataServerBoundCertHelper methods.
+ virtual void StartFetching(const FetchResultCallback& callback) OVERRIDE;
+ virtual void DeleteServerBoundCert(const std::string& server_id) OVERRIDE;
+
+ private:
+ virtual ~CannedBrowsingDataServerBoundCertHelper();
+
+ void FinishFetching();
+
+ typedef std::map<std::string, net::ServerBoundCertStore::ServerBoundCert>
+ ServerBoundCertMap;
+ ServerBoundCertMap server_bound_cert_map_;
+
+ FetchResultCallback completion_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CannedBrowsingDataServerBoundCertHelper);
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_BROWSING_DATA_SERVER_BOUND_CERT_HELPER_H_
diff --git a/chrome/browser/browsing_data/browsing_data_server_bound_cert_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_server_bound_cert_helper_unittest.cc
new file mode 100644
index 0000000..b1fd4bf
--- /dev/null
+++ b/chrome/browser/browsing_data/browsing_data_server_bound_cert_helper_unittest.cc
@@ -0,0 +1,159 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.h"
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread.h"
+#include "net/base/server_bound_cert_service.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserThread;
+
+class BrowsingDataServerBoundCertHelperTest : public testing::Test {
+ public:
+ virtual void SetUp() {
+ ui_thread_.reset(new content::TestBrowserThread(BrowserThread::UI,
+ &message_loop_));
+ io_thread_.reset(new content::TestBrowserThread(BrowserThread::IO,
+ &message_loop_));
+ testing_profile_.reset(new TestingProfile());
+ testing_profile_->CreateRequestContext();
+ }
+
+ void CreateCertsForTest() {
+ net::URLRequestContext* context =
+ testing_profile_->GetRequestContext()->GetURLRequestContext();
+ net::ServerBoundCertStore* cert_store =
+ context->server_bound_cert_service()->GetCertStore();
+ cert_store->SetServerBoundCert("https://www.google.com:443",
+ net::CLIENT_CERT_RSA_SIGN,
+ base::Time(), base::Time(),
+ "key", "cert");
+ cert_store->SetServerBoundCert("https://www.youtube.com:443",
+ net::CLIENT_CERT_RSA_SIGN,
+ base::Time(), base::Time(),
+ "key", "cert");
+ }
+
+ void FetchCallback(
+ const net::ServerBoundCertStore::ServerBoundCertList& certs) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ server_bound_cert_list_ = certs;
+ MessageLoop::current()->Quit();
+ }
+
+ protected:
+ MessageLoop message_loop_;
+ scoped_ptr<content::TestBrowserThread> ui_thread_;
+ scoped_ptr<content::TestBrowserThread> io_thread_;
+ scoped_ptr<TestingProfile> testing_profile_;
+
+ net::ServerBoundCertStore::ServerBoundCertList server_bound_cert_list_;
+};
+
+TEST_F(BrowsingDataServerBoundCertHelperTest, FetchData) {
+ CreateCertsForTest();
+ scoped_refptr<BrowsingDataServerBoundCertHelper> helper(
+ BrowsingDataServerBoundCertHelper::Create(testing_profile_.get()));
+
+ helper->StartFetching(
+ base::Bind(&BrowsingDataServerBoundCertHelperTest::FetchCallback,
+ base::Unretained(this)));
+
+ // Blocks until BrowsingDataServerBoundCertHelperTest::FetchCallback is
+ // notified.
+ MessageLoop::current()->Run();
+
+ ASSERT_EQ(2UL, server_bound_cert_list_.size());
+ net::ServerBoundCertStore::ServerBoundCertList::const_iterator it =
+ server_bound_cert_list_.begin();
+
+ // Correct because fetching server_bound_cert_list_ will get them out in the
+ // same order CreateCertsForTest put them in.
+ ASSERT_TRUE(it != server_bound_cert_list_.end());
+ EXPECT_EQ("https://www.google.com:443", it->server_identifier());
+
+ ASSERT_TRUE(++it != server_bound_cert_list_.end());
+ EXPECT_EQ("https://www.youtube.com:443", it->server_identifier());
+
+ ASSERT_TRUE(++it == server_bound_cert_list_.end());
+}
+
+TEST_F(BrowsingDataServerBoundCertHelperTest, DeleteCert) {
+ CreateCertsForTest();
+ scoped_refptr<BrowsingDataServerBoundCertHelper> helper(
+ BrowsingDataServerBoundCertHelper::Create(testing_profile_.get()));
+
+ helper->DeleteServerBoundCert("https://www.google.com:443");
+
+ helper->StartFetching(
+ base::Bind(&BrowsingDataServerBoundCertHelperTest::FetchCallback,
+ base::Unretained(this)));
+ MessageLoop::current()->Run();
+
+ ASSERT_EQ(1UL, server_bound_cert_list_.size());
+ net::ServerBoundCertStore::ServerBoundCertList::const_iterator it =
+ server_bound_cert_list_.begin();
+
+ ASSERT_TRUE(it != server_bound_cert_list_.end());
+ EXPECT_EQ("https://www.youtube.com:443", it->server_identifier());
+
+ ASSERT_TRUE(++it == server_bound_cert_list_.end());
+
+ helper->DeleteServerBoundCert("https://www.youtube.com:443");
+
+ helper->StartFetching(
+ base::Bind(&BrowsingDataServerBoundCertHelperTest::FetchCallback,
+ base::Unretained(this)));
+ MessageLoop::current()->Run();
+ ASSERT_EQ(0UL, server_bound_cert_list_.size());
+}
+
+TEST_F(BrowsingDataServerBoundCertHelperTest, CannedUnique) {
+ std::string origin = "https://www.google.com:443";
+
+ scoped_refptr<CannedBrowsingDataServerBoundCertHelper> helper(
+ new CannedBrowsingDataServerBoundCertHelper());
+
+ ASSERT_TRUE(helper->empty());
+ helper->AddServerBoundCert(net::ServerBoundCertStore::ServerBoundCert(
+ origin, net::CLIENT_CERT_RSA_SIGN, base::Time(), base::Time(), "key",
+ "cert"));
+ helper->AddServerBoundCert(net::ServerBoundCertStore::ServerBoundCert(
+ origin, net::CLIENT_CERT_ECDSA_SIGN, base::Time(), base::Time(), "key",
+ "cert"));
+
+ helper->StartFetching(
+ base::Bind(&BrowsingDataServerBoundCertHelperTest::FetchCallback,
+ base::Unretained(this)));
+ MessageLoop::current()->Run();
+
+ ASSERT_EQ(1UL, server_bound_cert_list_.size());
+ net::ServerBoundCertStore::ServerBoundCert& cert =
+ server_bound_cert_list_.front();
+
+ EXPECT_EQ("https://www.google.com:443", cert.server_identifier());
+ EXPECT_EQ(net::CLIENT_CERT_ECDSA_SIGN, cert.type());
+}
+
+TEST_F(BrowsingDataServerBoundCertHelperTest, CannedEmpty) {
+ std::string origin = "https://www.google.com";
+
+ scoped_refptr<CannedBrowsingDataServerBoundCertHelper> helper(
+ new CannedBrowsingDataServerBoundCertHelper());
+
+ ASSERT_TRUE(helper->empty());
+ helper->AddServerBoundCert(net::ServerBoundCertStore::ServerBoundCert(
+ origin, net::CLIENT_CERT_RSA_SIGN, base::Time(), base::Time(), "key",
+ "cert"));
+ ASSERT_FALSE(helper->empty());
+ helper->Reset();
+ ASSERT_TRUE(helper->empty());
+}
diff --git a/chrome/browser/browsing_data/local_data_container.cc b/chrome/browser/browsing_data/local_data_container.cc
new file mode 100644
index 0000000..d3d76a9
--- /dev/null
+++ b/chrome/browser/browsing_data/local_data_container.cc
@@ -0,0 +1,184 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/local_data_container.h"
+
+#include "base/bind.h"
+#include "base/memory/linked_ptr.h"
+#include "chrome/browser/browsing_data/browsing_data_cookie_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.h"
+#include "chrome/browser/content_settings/cookie_settings.h"
+#include "chrome/browser/cookies_tree_model.h"
+#include "net/cookies/canonical_cookie.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// LocalDataContainer, public:
+
+LocalDataContainer::LocalDataContainer(
+ const std::string& app_name,
+ const std::string& app_id,
+ BrowsingDataCookieHelper* cookie_helper,
+ BrowsingDataDatabaseHelper* database_helper,
+ BrowsingDataLocalStorageHelper* local_storage_helper,
+ BrowsingDataLocalStorageHelper* session_storage_helper,
+ BrowsingDataAppCacheHelper* appcache_helper,
+ BrowsingDataIndexedDBHelper* indexed_db_helper,
+ BrowsingDataFileSystemHelper* file_system_helper,
+ BrowsingDataQuotaHelper* quota_helper,
+ BrowsingDataServerBoundCertHelper* server_bound_cert_helper)
+ : app_name_(app_name),
+ app_id_(app_id),
+ appcache_helper_(appcache_helper),
+ cookie_helper_(cookie_helper),
+ database_helper_(database_helper),
+ local_storage_helper_(local_storage_helper),
+ session_storage_helper_(session_storage_helper),
+ indexed_db_helper_(indexed_db_helper),
+ file_system_helper_(file_system_helper),
+ quota_helper_(quota_helper),
+ server_bound_cert_helper_(server_bound_cert_helper),
+ model_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {}
+
+LocalDataContainer::~LocalDataContainer() {}
+
+void LocalDataContainer::Init(CookiesTreeModel* model) {
+ DCHECK(!model_);
+ model_ = model;
+
+ DCHECK(cookie_helper_);
+ cookie_helper_->StartFetching(
+ base::Bind(&LocalDataContainer::OnCookiesModelInfoLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (database_helper_) {
+ database_helper_->StartFetching(
+ base::Bind(&LocalDataContainer::OnDatabaseModelInfoLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+
+ if (local_storage_helper_) {
+ local_storage_helper_->StartFetching(
+ base::Bind(&LocalDataContainer::OnLocalStorageModelInfoLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+
+ if (session_storage_helper_) {
+ session_storage_helper_->StartFetching(
+ base::Bind(&LocalDataContainer::OnSessionStorageModelInfoLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+
+ // TODO(michaeln): When all of the UI implementations have been updated, make
+ // this a required parameter.
+ if (appcache_helper_) {
+ appcache_helper_->StartFetching(
+ base::Bind(&LocalDataContainer::OnAppCacheModelInfoLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+
+ if (indexed_db_helper_) {
+ indexed_db_helper_->StartFetching(
+ base::Bind(&LocalDataContainer::OnIndexedDBModelInfoLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+
+ if (file_system_helper_) {
+ file_system_helper_->StartFetching(
+ base::Bind(&LocalDataContainer::OnFileSystemModelInfoLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+
+ if (quota_helper_) {
+ quota_helper_->StartFetching(
+ base::Bind(&LocalDataContainer::OnQuotaModelInfoLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+
+ if (server_bound_cert_helper_) {
+ server_bound_cert_helper_->StartFetching(
+ base::Bind(&LocalDataContainer::OnServerBoundCertModelInfoLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+}
+
+void LocalDataContainer::OnAppCacheModelInfoLoaded() {
+ using appcache::AppCacheInfo;
+ using appcache::AppCacheInfoCollection;
+ using appcache::AppCacheInfoVector;
+ typedef std::map<GURL, AppCacheInfoVector> InfoByOrigin;
+
+ scoped_refptr<AppCacheInfoCollection> appcache_info =
+ appcache_helper_->info_collection();
+ if (!appcache_info || appcache_info->infos_by_origin.empty())
+ return;
+
+ for (InfoByOrigin::const_iterator origin =
+ appcache_info->infos_by_origin.begin();
+ origin != appcache_info->infos_by_origin.end(); ++origin) {
+ std::list<AppCacheInfo>& info_list = appcache_info_[origin->first];
+ info_list.insert(
+ info_list.begin(), origin->second.begin(), origin->second.end());
+ }
+
+ model_->PopulateAppCacheInfo(this);
+}
+
+void LocalDataContainer::OnCookiesModelInfoLoaded(
+ const net::CookieList& cookie_list) {
+ cookie_list_.insert(cookie_list_.begin(),
+ cookie_list.begin(),
+ cookie_list.end());
+ DCHECK(model_);
+ model_->PopulateCookieInfo(this);
+}
+
+void LocalDataContainer::OnDatabaseModelInfoLoaded(
+ const DatabaseInfoList& database_info) {
+ database_info_list_ = database_info;
+ DCHECK(model_);
+ model_->PopulateDatabaseInfo(this);
+}
+
+void LocalDataContainer::OnLocalStorageModelInfoLoaded(
+ const LocalStorageInfoList& local_storage_info) {
+ local_storage_info_list_ = local_storage_info;
+ DCHECK(model_);
+ model_->PopulateLocalStorageInfo(this);
+}
+
+void LocalDataContainer::OnSessionStorageModelInfoLoaded(
+ const LocalStorageInfoList& session_storage_info) {
+ session_storage_info_list_ = session_storage_info;
+ DCHECK(model_);
+ model_->PopulateSessionStorageInfo(this);
+}
+
+void LocalDataContainer::OnIndexedDBModelInfoLoaded(
+ const IndexedDBInfoList& indexed_db_info) {
+ indexed_db_info_list_ = indexed_db_info;
+ DCHECK(model_);
+ model_->PopulateIndexedDBInfo(this);
+}
+
+void LocalDataContainer::OnFileSystemModelInfoLoaded(
+ const FileSystemInfoList& file_system_info) {
+ file_system_info_list_ = file_system_info;
+ DCHECK(model_);
+ model_->PopulateFileSystemInfo(this);
+}
+
+void LocalDataContainer::OnQuotaModelInfoLoaded(
+ const QuotaInfoList& quota_info) {
+ quota_info_list_ = quota_info;
+ DCHECK(model_);
+ model_->PopulateQuotaInfo(this);
+}
+
+void LocalDataContainer::OnServerBoundCertModelInfoLoaded(
+ const ServerBoundCertList& cert_list) {
+ server_bound_cert_list_ = cert_list;
+ DCHECK(model_);
+ model_->PopulateServerBoundCertInfo(this);
+}
diff --git a/chrome/browser/browsing_data/local_data_container.h b/chrome/browser/browsing_data/local_data_container.h
new file mode 100644
index 0000000..67edf7c
--- /dev/null
+++ b/chrome/browser/browsing_data/local_data_container.h
@@ -0,0 +1,141 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_LOCAL_DATA_CONTAINER_H_
+#define CHROME_BROWSER_BROWSING_DATA_LOCAL_DATA_CONTAINER_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/string16.h"
+#include "chrome/browser/browsing_data/browsing_data_appcache_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_cookie_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_database_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_file_system_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_quota_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.h"
+#include "net/base/server_bound_cert_store.h"
+
+class LocalDataContainer;
+class CookiesTreeModel;
+
+namespace net {
+class CanonicalCookie;
+}
+
+// Friendly typedefs for the multiple types of lists used in the model.
+namespace {
+
+typedef std::map<std::string, LocalDataContainer*> ContainerMap;
+typedef std::list<net::CanonicalCookie> CookieList;
+typedef std::list<BrowsingDataDatabaseHelper::DatabaseInfo> DatabaseInfoList;
+typedef std::list<BrowsingDataLocalStorageHelper::LocalStorageInfo>
+ LocalStorageInfoList;
+typedef std::list<BrowsingDataLocalStorageHelper::LocalStorageInfo>
+ SessionStorageInfoList;
+typedef std::list<BrowsingDataIndexedDBHelper::IndexedDBInfo>
+ IndexedDBInfoList;
+typedef std::list<BrowsingDataFileSystemHelper::FileSystemInfo>
+ FileSystemInfoList;
+typedef std::list<BrowsingDataQuotaHelper::QuotaInfo> QuotaInfoList;
+typedef net::ServerBoundCertStore::ServerBoundCertList ServerBoundCertList;
+typedef std::map<GURL, std::list<appcache::AppCacheInfo> > AppCacheInfoMap;
+
+} // namespace
+
+// LocalDataContainer ---------------------------------------------------------
+// This class is a wrapper around all the BrowsingData*Helper classes. Because
+// isolated applications have separate storage, we need different helper
+// instances. As such, this class contains the app name and id, along with the
+// helpers for all of the data types we need. The browser-wide "app id" will be
+// the empty string, as no app can have an empty id.
+class LocalDataContainer {
+ public:
+ LocalDataContainer(
+ const std::string& app_name,
+ const std::string& app_id,
+ BrowsingDataCookieHelper* cookie_helper,
+ BrowsingDataDatabaseHelper* database_helper,
+ BrowsingDataLocalStorageHelper* local_storage_helper,
+ BrowsingDataLocalStorageHelper* session_storage_helper,
+ BrowsingDataAppCacheHelper* appcache_helper,
+ BrowsingDataIndexedDBHelper* indexed_db_helper,
+ BrowsingDataFileSystemHelper* file_system_helper,
+ BrowsingDataQuotaHelper* quota_helper,
+ BrowsingDataServerBoundCertHelper* server_bound_cert_helper);
+ virtual ~LocalDataContainer();
+
+ // This method must be called to start the process of fetching the resources.
+ // The delegate passed in is called back to deliver the updates.
+ void Init(CookiesTreeModel* delegate);
+
+ const std::string& app_name() { return app_name_; }
+ const std::string& app_id() { return app_id_; }
+
+ private:
+ friend class CookiesTreeModel;
+ friend class CookieTreeAppCacheNode;
+ friend class CookieTreeCookieNode;
+ friend class CookieTreeDatabaseNode;
+ friend class CookieTreeLocalStorageNode;
+ friend class CookieTreeSessionStorageNode;
+ friend class CookieTreeIndexedDBNode;
+ friend class CookieTreeFileSystemNode;
+ friend class CookieTreeQuotaNode;
+ friend class CookieTreeServerBoundCertNode;
+
+ // Callback methods to be invoked when fetching the data is complete.
+ void OnAppCacheModelInfoLoaded();
+ void OnCookiesModelInfoLoaded(const net::CookieList& cookie_list);
+ void OnDatabaseModelInfoLoaded(const DatabaseInfoList& database_info);
+ void OnLocalStorageModelInfoLoaded(
+ const LocalStorageInfoList& local_storage_info);
+ void OnSessionStorageModelInfoLoaded(
+ const LocalStorageInfoList& local_storage_info);
+ void OnIndexedDBModelInfoLoaded(
+ const IndexedDBInfoList& indexed_db_info);
+ void OnFileSystemModelInfoLoaded(
+ const FileSystemInfoList& file_system_info);
+ void OnQuotaModelInfoLoaded(const QuotaInfoList& quota_info);
+ void OnServerBoundCertModelInfoLoaded(const ServerBoundCertList& cert_list);
+
+ // The app name and id, to which this container object is for.
+ std::string app_name_;
+ std::string app_id_;
+
+ // Pointers to the helper objects, needed to retreive all the types of locally
+ // stored data.
+ scoped_refptr<BrowsingDataAppCacheHelper> appcache_helper_;
+ scoped_refptr<BrowsingDataCookieHelper> cookie_helper_;
+ scoped_refptr<BrowsingDataDatabaseHelper> database_helper_;
+ scoped_refptr<BrowsingDataLocalStorageHelper> local_storage_helper_;
+ scoped_refptr<BrowsingDataLocalStorageHelper> session_storage_helper_;
+ scoped_refptr<BrowsingDataIndexedDBHelper> indexed_db_helper_;
+ scoped_refptr<BrowsingDataFileSystemHelper> file_system_helper_;
+ scoped_refptr<BrowsingDataQuotaHelper> quota_helper_;
+ scoped_refptr<BrowsingDataServerBoundCertHelper> server_bound_cert_helper_;
+
+ // Storage for all the data that was retrieved through the helper objects.
+ // The collected data is used for (re)creating the CookiesTreeModel.
+ AppCacheInfoMap appcache_info_;
+ CookieList cookie_list_;
+ DatabaseInfoList database_info_list_;
+ LocalStorageInfoList local_storage_info_list_;
+ LocalStorageInfoList session_storage_info_list_;
+ IndexedDBInfoList indexed_db_info_list_;
+ FileSystemInfoList file_system_info_list_;
+ QuotaInfoList quota_info_list_;
+ ServerBoundCertList server_bound_cert_list_;
+
+ // A delegate, which must outlive this object. The update callbacks use the
+ // delegate to deliver the updated data to the CookieTreeModel.
+ CookiesTreeModel* model_;
+
+ base::WeakPtrFactory<LocalDataContainer> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalDataContainer);
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_LOCAL_DATA_CONTAINER_H_
diff --git a/chrome/browser/browsing_data/mock_browsing_data_appcache_helper.cc b/chrome/browser/browsing_data/mock_browsing_data_appcache_helper.cc
new file mode 100644
index 0000000..52f87d3
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_appcache_helper.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/mock_browsing_data_appcache_helper.h"
+
+#include "base/callback.h"
+
+MockBrowsingDataAppCacheHelper::MockBrowsingDataAppCacheHelper(
+ Profile* profile)
+ : BrowsingDataAppCacheHelper(profile) {
+}
+
+MockBrowsingDataAppCacheHelper::~MockBrowsingDataAppCacheHelper() {
+}
+
+void MockBrowsingDataAppCacheHelper::StartFetching(
+ const base::Closure& completion_callback) {
+ completion_callback_ = completion_callback;
+}
+
+void MockBrowsingDataAppCacheHelper::DeleteAppCacheGroup(
+ const GURL& manifest_url) {
+}
diff --git a/chrome/browser/browsing_data/mock_browsing_data_appcache_helper.h b/chrome/browser/browsing_data/mock_browsing_data_appcache_helper.h
new file mode 100644
index 0000000..6306ba9
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_appcache_helper.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_APPCACHE_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_APPCACHE_HELPER_H_
+
+#include "base/callback_forward.h"
+#include "chrome/browser/browsing_data/browsing_data_appcache_helper.h"
+
+class MockBrowsingDataAppCacheHelper
+ : public BrowsingDataAppCacheHelper {
+ public:
+ explicit MockBrowsingDataAppCacheHelper(Profile* profile);
+
+ virtual void StartFetching(const base::Closure& completion_callback) OVERRIDE;
+ virtual void DeleteAppCacheGroup(const GURL& manifest_url) OVERRIDE;
+
+ private:
+ virtual ~MockBrowsingDataAppCacheHelper();
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_APPCACHE_HELPER_H_
diff --git a/chrome/browser/browsing_data/mock_browsing_data_cookie_helper.cc b/chrome/browser/browsing_data/mock_browsing_data_cookie_helper.cc
new file mode 100644
index 0000000..b8511e0
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_cookie_helper.cc
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/mock_browsing_data_cookie_helper.h"
+
+#include "base/logging.h"
+#include "net/cookies/canonical_cookie.h"
+#include "net/cookies/parsed_cookie.h"
+
+MockBrowsingDataCookieHelper::MockBrowsingDataCookieHelper(
+ net::URLRequestContextGetter* request_context_getter)
+ : BrowsingDataCookieHelper(request_context_getter) {
+}
+
+MockBrowsingDataCookieHelper::~MockBrowsingDataCookieHelper() {
+}
+
+void MockBrowsingDataCookieHelper::StartFetching(
+ const net::CookieMonster::GetCookieListCallback &callback) {
+ callback_ = callback;
+}
+
+void MockBrowsingDataCookieHelper::DeleteCookie(
+ const net::CanonicalCookie& cookie) {
+ std::string key = cookie.Name() + "=" + cookie.Value();
+ CHECK(cookies_.find(key) != cookies_.end());
+ cookies_[key] = false;
+}
+
+void MockBrowsingDataCookieHelper::AddCookieSamples(
+ const GURL& url, const std::string& cookie_line) {
+ typedef net::CookieList::const_iterator cookie_iterator;
+ net::ParsedCookie pc(cookie_line);
+ scoped_ptr<net::CanonicalCookie> cc(new net::CanonicalCookie(url, pc));
+
+ if (cc.get()) {
+ for (cookie_iterator cookie = cookie_list_.begin();
+ cookie != cookie_list_.end(); ++cookie) {
+ if (cookie->Name() == cc->Name() &&
+ cookie->Domain() == cc->Domain()&&
+ cookie->Path() == cc->Path()) {
+ return;
+ }
+ }
+ cookie_list_.push_back(*cc);
+ cookies_[cookie_line] = true;
+ }
+}
+
+void MockBrowsingDataCookieHelper::Notify() {
+ if (!callback_.is_null())
+ callback_.Run(cookie_list_);
+}
+
+void MockBrowsingDataCookieHelper::Reset() {
+ for (std::map<const std::string, bool>::iterator i = cookies_.begin();
+ i != cookies_.end(); ++i)
+ i->second = true;
+}
+
+bool MockBrowsingDataCookieHelper::AllDeleted() {
+ for (std::map<const std::string, bool>::const_iterator i = cookies_.begin();
+ i != cookies_.end(); ++i)
+ if (i->second)
+ return false;
+ return true;
+}
diff --git a/chrome/browser/browsing_data/mock_browsing_data_cookie_helper.h b/chrome/browser/browsing_data/mock_browsing_data_cookie_helper.h
new file mode 100644
index 0000000..7fef2a6
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_cookie_helper.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_COOKIE_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_COOKIE_HELPER_H_
+
+#include <map>
+#include <string>
+
+#include "chrome/browser/browsing_data/browsing_data_cookie_helper.h"
+#include "net/cookies/canonical_cookie.h"
+
+// Mock for BrowsingDataCookieHelper.
+class MockBrowsingDataCookieHelper : public BrowsingDataCookieHelper {
+ public:
+ explicit MockBrowsingDataCookieHelper(
+ net::URLRequestContextGetter* request_context_getter);
+
+ // BrowsingDataCookieHelper methods.
+ virtual void StartFetching(
+ const net::CookieMonster::GetCookieListCallback &callback) OVERRIDE;
+ virtual void DeleteCookie(const net::CanonicalCookie& cookie) OVERRIDE;
+
+ // Adds some cookie samples.
+ void AddCookieSamples(const GURL& url, const std::string& cookie_line);
+
+ // Notifies the callback.
+ void Notify();
+
+ // Marks all cookies as existing.
+ void Reset();
+
+ // Returns true if all cookies since the last Reset() invocation were
+ // deleted.
+ bool AllDeleted();
+
+ private:
+ virtual ~MockBrowsingDataCookieHelper();
+
+ net::CookieMonster::GetCookieListCallback callback_;
+
+ net::CookieList cookie_list_;
+
+ // Stores which cookies exist.
+ std::map<const std::string, bool> cookies_;
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_COOKIE_HELPER_H_
diff --git a/chrome/browser/browsing_data/mock_browsing_data_database_helper.cc b/chrome/browser/browsing_data/mock_browsing_data_database_helper.cc
new file mode 100644
index 0000000..b6fbdb5
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_database_helper.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/mock_browsing_data_database_helper.h"
+
+#include "base/callback.h"
+
+MockBrowsingDataDatabaseHelper::MockBrowsingDataDatabaseHelper(
+ Profile* profile)
+ : BrowsingDataDatabaseHelper(profile) {
+}
+
+MockBrowsingDataDatabaseHelper::~MockBrowsingDataDatabaseHelper() {
+}
+
+void MockBrowsingDataDatabaseHelper::StartFetching(
+ const base::Callback<void(const std::list<DatabaseInfo>&)>& callback) {
+ callback_ = callback;
+}
+
+void MockBrowsingDataDatabaseHelper::DeleteDatabase(
+ const std::string& origin,
+ const std::string& name) {
+ std::string key = origin + ":" + name;
+ CHECK(databases_.find(key) != databases_.end());
+ last_deleted_origin_ = origin;
+ last_deleted_db_ = name;
+ databases_[key] = false;
+}
+
+void MockBrowsingDataDatabaseHelper::AddDatabaseSamples() {
+ response_.push_back(BrowsingDataDatabaseHelper::DatabaseInfo(
+ "gdbhost1", "db1", "http_gdbhost1_1", "description 1",
+ "http://gdbhost1:1/", 1, base::Time()));
+ databases_["http_gdbhost1_1:db1"] = true;
+ response_.push_back(BrowsingDataDatabaseHelper::DatabaseInfo(
+ "gdbhost2", "db2", "http_gdbhost2_2", "description 2",
+ "http://gdbhost2:2/", 2, base::Time()));
+ databases_["http_gdbhost2_2:db2"] = true;
+}
+
+void MockBrowsingDataDatabaseHelper::Notify() {
+ CHECK_EQ(false, callback_.is_null());
+ callback_.Run(response_);
+}
+
+void MockBrowsingDataDatabaseHelper::Reset() {
+ for (std::map<const std::string, bool>::iterator i = databases_.begin();
+ i != databases_.end(); ++i)
+ i->second = true;
+}
+
+bool MockBrowsingDataDatabaseHelper::AllDeleted() {
+ for (std::map<const std::string, bool>::const_iterator i = databases_.begin();
+ i != databases_.end(); ++i)
+ if (i->second)
+ return false;
+ return true;
+}
diff --git a/chrome/browser/browsing_data/mock_browsing_data_database_helper.h b/chrome/browser/browsing_data/mock_browsing_data_database_helper.h
new file mode 100644
index 0000000..9e6ea9f
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_database_helper.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_DATABASE_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_DATABASE_HELPER_H_
+
+#include <list>
+#include <map>
+
+#include "base/callback.h"
+#include "chrome/browser/browsing_data/browsing_data_database_helper.h"
+
+// Mock for BrowsingDataDatabaseHelper.
+// Use AddDatabaseSamples() or add directly to response_ list, then call
+// Notify().
+class MockBrowsingDataDatabaseHelper : public BrowsingDataDatabaseHelper {
+ public:
+ explicit MockBrowsingDataDatabaseHelper(Profile* profile);
+
+ virtual void StartFetching(
+ const base::Callback<void(const std::list<DatabaseInfo>&)>& callback)
+ OVERRIDE;
+
+ virtual void DeleteDatabase(const std::string& origin,
+ const std::string& name) OVERRIDE;
+
+ // Adds some DatabaseInfo samples.
+ void AddDatabaseSamples();
+
+ // Notifies the callback.
+ void Notify();
+
+ // Marks all databases as existing.
+ void Reset();
+
+ // Returns true if all databases since the last Reset() invokation were
+ // deleted.
+ bool AllDeleted();
+
+ std::string last_deleted_origin_;
+
+ std::string last_deleted_db_;
+
+ private:
+ virtual ~MockBrowsingDataDatabaseHelper();
+
+ base::Callback<void(const std::list<DatabaseInfo>&)> callback_;
+
+ // Stores which databases exist.
+ std::map<const std::string, bool> databases_;
+
+ std::list<DatabaseInfo> response_;
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_DATABASE_HELPER_H_
diff --git a/chrome/browser/browsing_data/mock_browsing_data_file_system_helper.cc b/chrome/browser/browsing_data/mock_browsing_data_file_system_helper.cc
new file mode 100644
index 0000000..1e637bc
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_file_system_helper.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 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 "base/callback.h"
+#include "base/logging.h"
+#include "chrome/browser/browsing_data/mock_browsing_data_file_system_helper.h"
+
+MockBrowsingDataFileSystemHelper::MockBrowsingDataFileSystemHelper(
+ Profile* profile) {
+}
+
+MockBrowsingDataFileSystemHelper::~MockBrowsingDataFileSystemHelper() {
+}
+
+void MockBrowsingDataFileSystemHelper::StartFetching(
+ const base::Callback<void(const std::list<FileSystemInfo>&)>& callback) {
+ callback_ = callback;
+}
+
+void MockBrowsingDataFileSystemHelper::DeleteFileSystemOrigin(
+ const GURL& origin) {
+ std::string key = origin.spec();
+ CHECK(file_systems_.find(key) != file_systems_.end());
+ last_deleted_origin_ = origin;
+ file_systems_[key] = false;
+}
+
+void MockBrowsingDataFileSystemHelper::AddFileSystem(
+ const GURL& origin, bool has_persistent, bool has_temporary) {
+ response_.push_back(BrowsingDataFileSystemHelper::FileSystemInfo(
+ origin, has_persistent, has_temporary, 0, 0));
+ file_systems_[origin.spec()] = true;
+}
+
+void MockBrowsingDataFileSystemHelper::AddFileSystemSamples() {
+ AddFileSystem(GURL("http://fshost1:1/"), false, true);
+ AddFileSystem(GURL("http://fshost2:2/"), true, false);
+ AddFileSystem(GURL("http://fshost3:3/"), true, true);
+}
+
+void MockBrowsingDataFileSystemHelper::Notify() {
+ CHECK_EQ(false, callback_.is_null());
+ callback_.Run(response_);
+}
+
+void MockBrowsingDataFileSystemHelper::Reset() {
+ for (std::map<const std::string, bool>::iterator i = file_systems_.begin();
+ i != file_systems_.end(); ++i)
+ i->second = true;
+}
+
+bool MockBrowsingDataFileSystemHelper::AllDeleted() {
+ for (std::map<const std::string, bool>::const_iterator i =
+ file_systems_.begin();
+ i != file_systems_.end(); ++i) {
+ if (i->second)
+ return false;
+ }
+ return true;
+}
diff --git a/chrome/browser/browsing_data/mock_browsing_data_file_system_helper.h b/chrome/browser/browsing_data/mock_browsing_data_file_system_helper.h
new file mode 100644
index 0000000..2349019
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_file_system_helper.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_FILE_SYSTEM_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_FILE_SYSTEM_HELPER_H_
+
+#include <list>
+#include <map>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "chrome/browser/browsing_data/browsing_data_file_system_helper.h"
+
+// Mock for BrowsingDataFileSystemHelper.
+// Use AddFileSystemSamples() or add directly to response_ list, then call
+// Notify().
+class MockBrowsingDataFileSystemHelper : public BrowsingDataFileSystemHelper {
+ public:
+ explicit MockBrowsingDataFileSystemHelper(Profile* profile);
+
+ // BrowsingDataFileSystemHelper implementation.
+ virtual void StartFetching(const base::Callback<
+ void(const std::list<FileSystemInfo>&)>& callback) OVERRIDE;
+ virtual void DeleteFileSystemOrigin(const GURL& origin) OVERRIDE;
+
+ // Adds a specific filesystem.
+ void AddFileSystem(const GURL& origin,
+ bool has_persistent,
+ bool has_temporary);
+
+ // Adds some FilesystemInfo samples.
+ void AddFileSystemSamples();
+
+ // Notifies the callback.
+ void Notify();
+
+ // Marks all filesystems as existing.
+ void Reset();
+
+ // Returns true if all filesystemss since the last Reset() invocation were
+ // deleted.
+ bool AllDeleted();
+
+ GURL last_deleted_origin_;
+
+ private:
+ virtual ~MockBrowsingDataFileSystemHelper();
+
+ base::Callback<void(const std::list<FileSystemInfo>&)> callback_;
+
+ // Stores which filesystems exist.
+ std::map<const std::string, bool> file_systems_;
+
+ std::list<FileSystemInfo> response_;
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_FILE_SYSTEM_HELPER_H_
diff --git a/chrome/browser/browsing_data/mock_browsing_data_indexed_db_helper.cc b/chrome/browser/browsing_data/mock_browsing_data_indexed_db_helper.cc
new file mode 100644
index 0000000..a44e02c
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_indexed_db_helper.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/mock_browsing_data_indexed_db_helper.h"
+
+#include "base/callback.h"
+#include "base/logging.h"
+
+MockBrowsingDataIndexedDBHelper::MockBrowsingDataIndexedDBHelper() {
+}
+
+MockBrowsingDataIndexedDBHelper::~MockBrowsingDataIndexedDBHelper() {
+}
+
+void MockBrowsingDataIndexedDBHelper::StartFetching(
+ const base::Callback<void(const std::list<IndexedDBInfo>&)>& callback) {
+ callback_ = callback;
+}
+
+void MockBrowsingDataIndexedDBHelper::DeleteIndexedDB(
+ const GURL& origin) {
+ CHECK(origins_.find(origin) != origins_.end());
+ origins_[origin] = false;
+}
+
+void MockBrowsingDataIndexedDBHelper::AddIndexedDBSamples() {
+ const GURL kOrigin1("http://idbhost1:1/");
+ const GURL kOrigin2("http://idbhost2:2/");
+ response_.push_back(
+ BrowsingDataIndexedDBHelper::IndexedDBInfo(
+ kOrigin1, 1, base::Time()));
+ origins_[kOrigin1] = true;
+ response_.push_back(
+ BrowsingDataIndexedDBHelper::IndexedDBInfo(
+ kOrigin2, 2, base::Time()));
+ origins_[kOrigin2] = true;
+}
+
+void MockBrowsingDataIndexedDBHelper::Notify() {
+ CHECK_EQ(false, callback_.is_null());
+ callback_.Run(response_);
+}
+
+void MockBrowsingDataIndexedDBHelper::Reset() {
+ for (std::map<GURL, bool>::iterator i = origins_.begin();
+ i != origins_.end(); ++i)
+ i->second = true;
+}
+
+bool MockBrowsingDataIndexedDBHelper::AllDeleted() {
+ for (std::map<GURL, bool>::const_iterator i = origins_.begin();
+ i != origins_.end(); ++i)
+ if (i->second)
+ return false;
+ return true;
+}
diff --git a/chrome/browser/browsing_data/mock_browsing_data_indexed_db_helper.h b/chrome/browser/browsing_data/mock_browsing_data_indexed_db_helper.h
new file mode 100644
index 0000000..c24e1cb
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_indexed_db_helper.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_INDEXED_DB_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_INDEXED_DB_HELPER_H_
+
+#include <list>
+#include <map>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
+
+// Mock for BrowsingDataIndexedDBHelper.
+// Use AddIndexedDBSamples() or add directly to response_ list, then
+// call Notify().
+class MockBrowsingDataIndexedDBHelper
+ : public BrowsingDataIndexedDBHelper {
+ public:
+ MockBrowsingDataIndexedDBHelper();
+
+ // Adds some IndexedDBInfo samples.
+ void AddIndexedDBSamples();
+
+ // Notifies the callback.
+ void Notify();
+
+ // Marks all indexed db files as existing.
+ void Reset();
+
+ // Returns true if all indexed db files were deleted since the last
+ // Reset() invokation.
+ bool AllDeleted();
+
+ // BrowsingDataIndexedDBHelper.
+ virtual void StartFetching(
+ const base::Callback<void(const std::list<IndexedDBInfo>&)>&
+ callback) OVERRIDE;
+ virtual void DeleteIndexedDB(const GURL& origin) OVERRIDE;
+
+ private:
+ virtual ~MockBrowsingDataIndexedDBHelper();
+
+ base::Callback<void(const std::list<IndexedDBInfo>&)> callback_;
+ std::map<GURL, bool> origins_;
+ std::list<IndexedDBInfo> response_;
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_INDEXED_DB_HELPER_H_
diff --git a/chrome/browser/browsing_data/mock_browsing_data_local_storage_helper.cc b/chrome/browser/browsing_data/mock_browsing_data_local_storage_helper.cc
new file mode 100644
index 0000000..cf9cd28
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_local_storage_helper.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/mock_browsing_data_local_storage_helper.h"
+
+#include "base/callback.h"
+#include "base/logging.h"
+
+MockBrowsingDataLocalStorageHelper::MockBrowsingDataLocalStorageHelper(
+ Profile* profile)
+ : BrowsingDataLocalStorageHelper(profile) {
+}
+
+MockBrowsingDataLocalStorageHelper::~MockBrowsingDataLocalStorageHelper() {
+}
+
+void MockBrowsingDataLocalStorageHelper::StartFetching(
+ const base::Callback<void(const std::list<LocalStorageInfo>&)>& callback) {
+ callback_ = callback;
+}
+
+void MockBrowsingDataLocalStorageHelper::DeleteOrigin(
+ const GURL& origin) {
+ CHECK(origins_.find(origin) != origins_.end());
+ last_deleted_origin_ = origin;
+ origins_[origin] = false;
+}
+
+void MockBrowsingDataLocalStorageHelper::AddLocalStorageSamples() {
+ const GURL kOrigin1("http://host1:1/");
+ const GURL kOrigin2("http://host2:2/");
+ response_.push_back(
+ BrowsingDataLocalStorageHelper::LocalStorageInfo(
+ kOrigin1, 1, base::Time()));
+ origins_[kOrigin1] = true;
+ response_.push_back(
+ BrowsingDataLocalStorageHelper::LocalStorageInfo(
+ kOrigin2, 2, base::Time()));
+ origins_[kOrigin2] = true;
+}
+
+void MockBrowsingDataLocalStorageHelper::Notify() {
+ CHECK_EQ(false, callback_.is_null());
+ callback_.Run(response_);
+}
+
+void MockBrowsingDataLocalStorageHelper::Reset() {
+ for (std::map<const GURL, bool>::iterator i = origins_.begin();
+ i != origins_.end(); ++i)
+ i->second = true;
+}
+
+bool MockBrowsingDataLocalStorageHelper::AllDeleted() {
+ for (std::map<const GURL, bool>::const_iterator i =
+ origins_.begin(); i != origins_.end(); ++i)
+ if (i->second)
+ return false;
+ return true;
+}
diff --git a/chrome/browser/browsing_data/mock_browsing_data_local_storage_helper.h b/chrome/browser/browsing_data/mock_browsing_data_local_storage_helper.h
new file mode 100644
index 0000000..e72901ea
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_local_storage_helper.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_LOCAL_STORAGE_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_LOCAL_STORAGE_HELPER_H_
+
+#include <list>
+#include <map>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
+
+// Mock for BrowsingDataLocalStorageHelper.
+// Use AddLocalStorageSamples() or add directly to response_ list, then
+// call Notify().
+class MockBrowsingDataLocalStorageHelper
+ : public BrowsingDataLocalStorageHelper {
+ public:
+ explicit MockBrowsingDataLocalStorageHelper(Profile* profile);
+
+ // BrowsingDataLocalStorageHelper implementation.
+ virtual void StartFetching(
+ const base::Callback<void(const std::list<LocalStorageInfo>&)>& callback)
+ OVERRIDE;
+ virtual void DeleteOrigin(const GURL& origin) OVERRIDE;
+
+ // Adds some LocalStorageInfo samples.
+ void AddLocalStorageSamples();
+
+ // Notifies the callback.
+ void Notify();
+
+ // Marks all local storage files as existing.
+ void Reset();
+
+ // Returns true if all local storage files were deleted since the last Reset()
+ // invocation.
+ bool AllDeleted();
+
+ GURL last_deleted_origin_;
+
+ private:
+ virtual ~MockBrowsingDataLocalStorageHelper();
+
+ base::Callback<void(const std::list<LocalStorageInfo>&)> callback_;
+
+ std::map<const GURL, bool> origins_;
+
+ std::list<LocalStorageInfo> response_;
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_LOCAL_STORAGE_HELPER_H_
diff --git a/chrome/browser/browsing_data/mock_browsing_data_quota_helper.cc b/chrome/browser/browsing_data/mock_browsing_data_quota_helper.cc
new file mode 100644
index 0000000..6ff8518
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_quota_helper.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/mock_browsing_data_quota_helper.h"
+
+using content::BrowserThread;
+
+MockBrowsingDataQuotaHelper::MockBrowsingDataQuotaHelper(Profile* profile)
+ : BrowsingDataQuotaHelper(BrowserThread::GetMessageLoopProxyForThread(
+ BrowserThread::IO)) {}
+
+MockBrowsingDataQuotaHelper::~MockBrowsingDataQuotaHelper() {}
+
+void MockBrowsingDataQuotaHelper::StartFetching(
+ const FetchResultCallback& callback) {
+ callback_ = callback;
+}
+
+void MockBrowsingDataQuotaHelper::RevokeHostQuota(const std::string& host) {
+}
+
+void MockBrowsingDataQuotaHelper::AddHost(
+ const std::string& host,
+ int64 temporary_usage,
+ int64 persistent_usage) {
+ response_.push_back(QuotaInfo(
+ host,
+ temporary_usage,
+ persistent_usage));
+}
+
+void MockBrowsingDataQuotaHelper::AddQuotaSamples() {
+ AddHost("quotahost1", 1, 2);
+ AddHost("quotahost2", 10, 20);
+}
+
+void MockBrowsingDataQuotaHelper::Notify() {
+ CHECK_EQ(false, callback_.is_null());
+ callback_.Run(response_);
+ callback_.Reset();
+ response_.clear();
+}
diff --git a/chrome/browser/browsing_data/mock_browsing_data_quota_helper.h b/chrome/browser/browsing_data/mock_browsing_data_quota_helper.h
new file mode 100644
index 0000000..21f4570
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_quota_helper.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_QUOTA_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_QUOTA_HELPER_H_
+
+#include <list>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "chrome/browser/browsing_data/browsing_data_quota_helper.h"
+
+class MockBrowsingDataQuotaHelper : public BrowsingDataQuotaHelper {
+ public:
+ explicit MockBrowsingDataQuotaHelper(Profile* profile);
+
+ virtual void StartFetching(const FetchResultCallback& callback) OVERRIDE;
+ virtual void RevokeHostQuota(const std::string& host) OVERRIDE;
+
+ void AddHost(const std::string& host,
+ int64 temporary_usage,
+ int64 persistent_usage);
+ void AddQuotaSamples();
+ void Notify();
+
+ private:
+ virtual ~MockBrowsingDataQuotaHelper();
+
+ FetchResultCallback callback_;
+ std::list<QuotaInfo> response_;
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_QUOTA_HELPER_H_
diff --git a/chrome/browser/browsing_data/mock_browsing_data_server_bound_cert_helper.cc b/chrome/browser/browsing_data/mock_browsing_data_server_bound_cert_helper.cc
new file mode 100644
index 0000000..6cb78ed
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_server_bound_cert_helper.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 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 "chrome/browser/browsing_data/mock_browsing_data_server_bound_cert_helper.h"
+
+#include "base/logging.h"
+
+MockBrowsingDataServerBoundCertHelper::MockBrowsingDataServerBoundCertHelper()
+ : BrowsingDataServerBoundCertHelper() {}
+
+MockBrowsingDataServerBoundCertHelper::
+~MockBrowsingDataServerBoundCertHelper() {}
+
+void MockBrowsingDataServerBoundCertHelper::StartFetching(
+ const FetchResultCallback& callback) {
+ callback_ = callback;
+}
+
+void MockBrowsingDataServerBoundCertHelper::DeleteServerBoundCert(
+ const std::string& server_id) {
+ CHECK(server_bound_certs_.find(server_id) != server_bound_certs_.end());
+ server_bound_certs_[server_id] = false;
+}
+
+void MockBrowsingDataServerBoundCertHelper::AddServerBoundCertSample(
+ const std::string& server_id) {
+ DCHECK(server_bound_certs_.find(server_id) == server_bound_certs_.end());
+ server_bound_cert_list_.push_back(
+ net::ServerBoundCertStore::ServerBoundCert(
+ server_id, net::CLIENT_CERT_ECDSA_SIGN,
+ base::Time(), base::Time(), "key", "cert"));
+ server_bound_certs_[server_id] = true;
+}
+
+void MockBrowsingDataServerBoundCertHelper::Notify() {
+ net::ServerBoundCertStore::ServerBoundCertList cert_list;
+ for (net::ServerBoundCertStore::ServerBoundCertList::iterator i =
+ server_bound_cert_list_.begin();
+ i != server_bound_cert_list_.end(); ++i) {
+ if (server_bound_certs_[i->server_identifier()])
+ cert_list.push_back(*i);
+ }
+ callback_.Run(cert_list);
+}
+
+void MockBrowsingDataServerBoundCertHelper::Reset() {
+ for (std::map<const std::string, bool>::iterator i =
+ server_bound_certs_.begin();
+ i != server_bound_certs_.end(); ++i)
+ i->second = true;
+}
+
+bool MockBrowsingDataServerBoundCertHelper::AllDeleted() {
+ for (std::map<const std::string, bool>::const_iterator i =
+ server_bound_certs_.begin();
+ i != server_bound_certs_.end(); ++i)
+ if (i->second)
+ return false;
+ return true;
+}
diff --git a/chrome/browser/browsing_data/mock_browsing_data_server_bound_cert_helper.h b/chrome/browser/browsing_data/mock_browsing_data_server_bound_cert_helper.h
new file mode 100644
index 0000000..33edee3
--- /dev/null
+++ b/chrome/browser/browsing_data/mock_browsing_data_server_bound_cert_helper.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_SERVER_BOUND_CERT_HELPER_H_
+#define CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_SERVER_BOUND_CERT_HELPER_H_
+
+#include <map>
+#include <string>
+
+#include "chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.h"
+
+// Mock for BrowsingDataServerBoundCertHelper.
+class MockBrowsingDataServerBoundCertHelper
+ : public BrowsingDataServerBoundCertHelper {
+ public:
+ explicit MockBrowsingDataServerBoundCertHelper();
+
+ // BrowsingDataServerBoundCertHelper methods.
+ virtual void StartFetching(const FetchResultCallback& callback) OVERRIDE;
+ virtual void DeleteServerBoundCert(const std::string& server_id) OVERRIDE;
+
+ // Adds a server_bound_cert sample.
+ void AddServerBoundCertSample(const std::string& server_id);
+
+ // Notifies the callback.
+ void Notify();
+
+ // Marks all server_bound_certs as existing.
+ void Reset();
+
+ // Returns true if all server_bound_certs since the last Reset() invocation
+ // were deleted.
+ bool AllDeleted();
+
+ private:
+ virtual ~MockBrowsingDataServerBoundCertHelper();
+
+ FetchResultCallback callback_;
+
+ net::ServerBoundCertStore::ServerBoundCertList server_bound_cert_list_;
+
+ // Stores which server_bound_certs exist.
+ std::map<const std::string, bool> server_bound_certs_;
+};
+
+#endif // CHROME_BROWSER_BROWSING_DATA_MOCK_BROWSING_DATA_SERVER_BOUND_CERT_HELPER_H_