// Copyright (c) 2006-2008 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_remover.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/download/download_manager.h" #include "chrome/browser/history/history.h" #include "chrome/browser/in_process_webkit/webkit_context.h" #include "chrome/browser/profile.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/net/url_request_context_getter.h" #include "chrome/browser/password_manager/password_store.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/browser/sessions/session_service.h" #include "chrome/browser/sessions/tab_restore_service.h" #include "chrome/browser/webdata/web_data_service.h" #include "chrome/common/notification_service.h" #include "net/base/cookie_monster.h" #include "net/disk_cache/disk_cache.h" #include "net/http/http_cache.h" #include "net/url_request/url_request_context.h" #include "webkit/glue/password_form.h" // Done so that we can use PostTask on BrowsingDataRemovers and not have // BrowsingDataRemover implement RefCounted. template <> struct RunnableMethodTraits { void RetainCallee(BrowsingDataRemover* remover) {} void ReleaseCallee(BrowsingDataRemover* remover) {} }; bool BrowsingDataRemover::removing_ = false; BrowsingDataRemover::BrowsingDataRemover(Profile* profile, base::Time delete_begin, base::Time delete_end) : profile_(profile), delete_begin_(delete_begin), delete_end_(delete_end), waiting_for_clear_history_(false), waiting_for_clear_cache_(false) { DCHECK(profile); } BrowsingDataRemover::BrowsingDataRemover(Profile* profile, TimePeriod time_period, base::Time delete_end) : profile_(profile), delete_begin_(CalculateBeginDeleteTime(time_period)), delete_end_(delete_end), waiting_for_clear_history_(false), waiting_for_clear_cache_(false) { DCHECK(profile); } BrowsingDataRemover::~BrowsingDataRemover() { DCHECK(all_done()); } void BrowsingDataRemover::Remove(int remove_mask) { DCHECK(!removing_); removing_ = true; if (remove_mask & REMOVE_HISTORY) { HistoryService* history_service = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); if (history_service) { UserMetrics::RecordAction("ClearBrowsingData_History", profile_); waiting_for_clear_history_ = true; history_service->ExpireHistoryBetween(delete_begin_, delete_end_, &request_consumer_, NewCallback(this, &BrowsingDataRemover::OnHistoryDeletionDone)); } // As part of history deletion we also delete the auto-generated keywords. TemplateURLModel* keywords_model = profile_->GetTemplateURLModel(); if (keywords_model && !keywords_model->loaded()) { registrar_.Add(this, NotificationType::TEMPLATE_URL_MODEL_LOADED, Source(keywords_model)); keywords_model->Load(); } else if (keywords_model) { keywords_model->RemoveAutoGeneratedBetween(delete_begin_, delete_end_); } // 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 = profile_->GetTabRestoreService(); if (tab_service) { tab_service->ClearEntries(); tab_service->DeleteLastSession(); } // We also delete the last session when we delete the history. SessionService* session_service = profile_->GetSessionService(); if (session_service) session_service->DeleteLastSession(); } if (remove_mask & REMOVE_DOWNLOADS) { UserMetrics::RecordAction("ClearBrowsingData_Downloads", profile_); DownloadManager* download_manager = profile_->GetDownloadManager(); download_manager->RemoveDownloadsBetween(delete_begin_, delete_end_); download_manager->ClearLastDownloadPath(); } if (remove_mask & REMOVE_COOKIES) { UserMetrics::RecordAction("ClearBrowsingData_Cookies", profile_); // Since we are running on the UI thread don't call GetURLRequestContext(). net::CookieMonster* cookie_monster = profile_->GetRequestContext()->GetCookieStore()->GetCookieMonster(); if (cookie_monster) cookie_monster->DeleteAllCreatedBetween(delete_begin_, delete_end_, true); profile_->GetWebKitContext()->DeleteDataModifiedSince(delete_begin_); } if (remove_mask & REMOVE_PASSWORDS) { UserMetrics::RecordAction("ClearBrowsingData_Passwords", profile_); PasswordStore* password_store = profile_->GetPasswordStore(Profile::EXPLICIT_ACCESS); password_store->RemoveLoginsCreatedBetween(delete_begin_, delete_end_); } if (remove_mask & REMOVE_FORM_DATA) { UserMetrics::RecordAction("ClearBrowsingData_Autofill", profile_); WebDataService* web_data_service = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); web_data_service->RemoveFormElementsAddedBetween(delete_begin_, delete_end_); } if (remove_mask & REMOVE_CACHE) { // Invoke ClearBrowsingDataView::ClearCache on the IO thread. waiting_for_clear_cache_ = true; UserMetrics::RecordAction("ClearBrowsingData_Cache", profile_); URLRequestContextGetter* main_context_getter = profile_->GetRequestContext(); URLRequestContextGetter* media_context_getter = profile_->GetRequestContextForMedia(); // Balanced in ClearCacheOnIOThread(). main_context_getter->AddRef(); media_context_getter->AddRef(); ChromeThread::PostTask( ChromeThread::IO, FROM_HERE, NewRunnableMethod( this, &BrowsingDataRemover::ClearCacheOnIOThread, main_context_getter, media_context_getter, delete_begin_, delete_end_)); } 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(); } base::Time BrowsingDataRemover::CalculateBeginDeleteTime( TimePeriod time_period) { base::TimeDelta diff; base::Time delete_begin_time = base::Time::Now(); switch (time_period) { 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; } void BrowsingDataRemover::Observe(NotificationType type, const NotificationSource& source, const 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 == NotificationType::TEMPLATE_URL_MODEL_LOADED); TemplateURLModel* model = Source(source).ptr(); if (model->profile() == profile_->GetOriginalProfile()) { registrar_.RemoveAll(); model->RemoveAutoGeneratedBetween(delete_begin_, delete_end_); NotifyAndDeleteIfDone(); } } void BrowsingDataRemover::NotifyAndDeleteIfDone() { // TODO(brettw) bug 1139736: see TODO in Observe() above. if (!all_done()) return; removing_ = false; 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::ClearedCache() { waiting_for_clear_cache_ = false; NotifyAndDeleteIfDone(); } void BrowsingDataRemover::ClearCacheOnIOThread( URLRequestContextGetter* main_context_getter, URLRequestContextGetter* media_context_getter, base::Time delete_begin, base::Time delete_end) { // This function should be called on the IO thread. DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); // Get a pointer to the cache. net::HttpTransactionFactory* factory = main_context_getter->GetURLRequestContext()->http_transaction_factory(); disk_cache::Backend* cache = factory->GetCache()->GetBackend(); // |cache| can be null if it cannot be initialized. if (cache) { if (delete_begin.is_null()) cache->DoomAllEntries(); else cache->DoomEntriesBetween(delete_begin, delete_end); } // Get a pointer to the media cache. factory = media_context_getter->GetURLRequestContext()-> http_transaction_factory(); cache = factory->GetCache()->GetBackend(); // |cache| can be null if it cannot be initialized. if (cache) { if (delete_begin.is_null()) cache->DoomAllEntries(); else cache->DoomEntriesBetween(delete_begin, delete_end); } // Balance the AddRef()s done on the UI thread by Remove(). main_context_getter->Release(); media_context_getter->Release(); // Notify the UI thread that we are done. ChromeThread::PostTask( ChromeThread::UI, FROM_HERE, NewRunnableMethod(this, &BrowsingDataRemover::ClearedCache)); }