diff options
author | markusheintz@chromium.org <markusheintz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-19 19:22:47 +0000 |
---|---|---|
committer | markusheintz@chromium.org <markusheintz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-19 19:22:47 +0000 |
commit | b0cb5e82de3a8195c91aecdac90bcd8fcc77bdd8 (patch) | |
tree | 15a6587d385b54fe41e83b724747eb0d2dc4388e /chrome/browser/browsing_data | |
parent | 3cbacdcbc6765a739ca5a1edf13c2dd20be13bf1 (diff) | |
download | chromium_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')
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_ |