| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/history/in_memory_url_index.h" | 5 #include "chrome/browser/history/in_memory_url_index.h" |
| 6 | 6 |
| 7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
| 8 #include "base/utf_string_conversions.h" | 8 #include "base/utf_string_conversions.h" |
| 9 #include "chrome/browser/history/history_notifications.h" | 9 #include "chrome/browser/history/history_notifications.h" |
| 10 #include "chrome/browser/history/history_service_factory.h" | 10 #include "chrome/browser/history/history_service_factory.h" |
| 11 #include "chrome/browser/history/in_memory_url_cache_database.h" |
| 11 #include "chrome/browser/history/url_database.h" | 12 #include "chrome/browser/history/url_database.h" |
| 12 #include "chrome/browser/history/url_index_private_data.h" | 13 #include "chrome/browser/history/url_index_private_data.h" |
| 13 #include "chrome/browser/profiles/profile.h" | 14 #include "chrome/browser/profiles/profile.h" |
| 14 #include "chrome/common/chrome_notification_types.h" | 15 #include "chrome/common/chrome_notification_types.h" |
| 15 #include "chrome/common/url_constants.h" | |
| 16 #include "content/public/browser/browser_thread.h" | 16 #include "content/public/browser/browser_thread.h" |
| 17 #include "content/public/browser/notification_details.h" | 17 #include "content/public/browser/notification_details.h" |
| 18 #include "content/public/browser/notification_service.h" | 18 #include "content/public/browser/notification_service.h" |
| 19 #include "content/public/browser/notification_source.h" | 19 #include "content/public/browser/notification_source.h" |
| 20 | 20 |
| 21 using in_memory_url_index::InMemoryURLIndexCacheItem; | 21 using content::BrowserThread; |
| 22 | 22 |
| 23 namespace history { | 23 namespace history { |
| 24 | 24 |
| 25 // Called by DoSaveToCacheFile to delete any old cache file at |path| when | 25 // InMemoryURLIndex::Observer -------------------------------------------------- |
| 26 // there is no private data to save. Runs on the FILE thread. | 26 |
| 27 void DeleteCacheFile(const FilePath& path) { | 27 InMemoryURLIndex::Observer::Observer(InMemoryURLIndex* index) |
| 28 DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 28 : index_(index) { |
| 29 file_util::Delete(path, false); | 29 DCHECK(index); |
| 30 index_->AddObserver(this); |
| 30 } | 31 } |
| 31 | 32 |
| 32 // Initializes a whitelist of URL schemes. | 33 InMemoryURLIndex::Observer::~Observer() { |
| 33 void InitializeSchemeWhitelist(std::set<std::string>* whitelist) { | 34 index_->RemoveObserver(this); |
| 34 DCHECK(whitelist); | |
| 35 if (!whitelist->empty()) | |
| 36 return; // Nothing to do, already initialized. | |
| 37 whitelist->insert(std::string(chrome::kAboutScheme)); | |
| 38 whitelist->insert(std::string(chrome::kChromeUIScheme)); | |
| 39 whitelist->insert(std::string(chrome::kFileScheme)); | |
| 40 whitelist->insert(std::string(chrome::kFtpScheme)); | |
| 41 whitelist->insert(std::string(chrome::kHttpScheme)); | |
| 42 whitelist->insert(std::string(chrome::kHttpsScheme)); | |
| 43 whitelist->insert(std::string(chrome::kMailToScheme)); | |
| 44 } | 35 } |
| 45 | 36 |
| 46 // RefCountedBool -------------------------------------------------------------- | 37 void InMemoryURLIndex::Observer::Loaded() { |
| 47 | 38 MessageLoop::current()->QuitNow(); |
| 48 RefCountedBool::~RefCountedBool() {} | 39 } |
| 49 | |
| 50 // Restore/SaveCacheObserver --------------------------------------------------- | |
| 51 | |
| 52 InMemoryURLIndex::RestoreCacheObserver::~RestoreCacheObserver() {} | |
| 53 | |
| 54 InMemoryURLIndex::SaveCacheObserver::~SaveCacheObserver() {} | |
| 55 | 40 |
| 56 // RebuildPrivateDataFromHistoryDBTask ----------------------------------------- | 41 // RebuildPrivateDataFromHistoryDBTask ----------------------------------------- |
| 57 | 42 |
| 58 InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask:: | 43 InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask:: |
| 59 RebuildPrivateDataFromHistoryDBTask( | 44 RebuildPrivateDataFromHistoryDBTask(InMemoryURLIndex* index) |
| 60 InMemoryURLIndex* index, | |
| 61 const std::string& languages, | |
| 62 const std::set<std::string>& scheme_whitelist) | |
| 63 : index_(index), | 45 : index_(index), |
| 64 languages_(languages), | |
| 65 scheme_whitelist_(scheme_whitelist), | |
| 66 succeeded_(false) { | 46 succeeded_(false) { |
| 67 } | 47 } |
| 68 | 48 |
| 49 InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask:: |
| 50 ~RebuildPrivateDataFromHistoryDBTask() { |
| 51 } |
| 52 |
| 69 bool InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask::RunOnDBThread( | 53 bool InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask::RunOnDBThread( |
| 70 HistoryBackend* backend, | 54 HistoryBackend* backend, |
| 71 HistoryDatabase* db) { | 55 HistoryDatabase* db) { |
| 72 data_ = URLIndexPrivateData::RebuildFromHistory(db, languages_, | 56 data_ = URLIndexPrivateData::RebuildFromHistory(db, index_->private_data()); |
| 73 scheme_whitelist_); | |
| 74 succeeded_ = data_.get() && !data_->Empty(); | 57 succeeded_ = data_.get() && !data_->Empty(); |
| 75 if (!succeeded_ && data_.get()) | |
| 76 data_->Clear(); | |
| 77 return true; | 58 return true; |
| 78 } | 59 } |
| 79 | 60 |
| 80 void InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask:: | 61 void InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask:: |
| 81 DoneRunOnMainThread() { | 62 DoneRunOnMainThread() { |
| 82 index_->DoneRebuidingPrivateDataFromHistoryDB(succeeded_, data_); | 63 index_->DoneRebuidingPrivateDataFromHistoryDB(succeeded_, data_); |
| 83 } | 64 } |
| 84 | 65 |
| 85 InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask:: | 66 // IndexUpdateItem ------------------------------------------------------------- |
| 86 ~RebuildPrivateDataFromHistoryDBTask() { | 67 |
| 68 InMemoryURLIndex::IndexUpdateItem::IndexUpdateItem(UpdateType update_type, |
| 69 URLRow row) |
| 70 : update_type(update_type), |
| 71 row(row) { |
| 87 } | 72 } |
| 88 | 73 |
| 74 InMemoryURLIndex::IndexUpdateItem::~IndexUpdateItem() {} |
| 75 |
| 89 // InMemoryURLIndex ------------------------------------------------------------ | 76 // InMemoryURLIndex ------------------------------------------------------------ |
| 90 | 77 |
| 91 InMemoryURLIndex::InMemoryURLIndex(Profile* profile, | 78 InMemoryURLIndex::InMemoryURLIndex(Profile* profile, |
| 92 const FilePath& history_dir, | 79 const FilePath& history_dir, |
| 93 const std::string& languages) | 80 const std::string& languages) |
| 94 : profile_(profile), | 81 : profile_(profile), |
| 82 languages_(languages), |
| 95 history_dir_(history_dir), | 83 history_dir_(history_dir), |
| 96 languages_(languages), | 84 private_data_(new URLIndexPrivateData(history_dir, languages)), |
| 97 private_data_(new URLIndexPrivateData), | 85 sequence_token_(BrowserThread::GetBlockingPool()->GetSequenceToken()), |
| 98 restore_cache_observer_(NULL), | 86 index_available_(false), |
| 99 save_cache_observer_(NULL), | |
| 100 shutdown_(false), | 87 shutdown_(false), |
| 101 needs_to_be_cached_(false) { | 88 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
| 102 InitializeSchemeWhitelist(&scheme_whitelist_); | |
| 103 if (profile) { | 89 if (profile) { |
| 104 // TODO(mrossetti): Register for language change notifications. | |
| 105 content::Source<Profile> source(profile); | 90 content::Source<Profile> source(profile); |
| 106 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URL_VISITED, source); | 91 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URL_VISITED, source); |
| 107 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, | 92 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, |
| 108 source); | 93 source); |
| 109 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, source); | 94 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, source); |
| 110 } | 95 } |
| 96 // Note: private_data_ will be reset after rebuilding from the history |
| 97 // database but the ownership of the database passes to the new |
| 98 // private_data_ instance so there is no need to re-register for the |
| 99 // following notification at that time. |
| 100 registrar_.Add(this, |
| 101 chrome::NOTIFICATION_IN_MEMORY_URL_CACHE_DATABASE_FAILURE, |
| 102 content::Source<InMemoryURLCacheDatabase>( |
| 103 private_data_->cache_db())); |
| 104 // TODO(mrossetti): Register for language change notifications. |
| 111 } | 105 } |
| 112 | 106 |
| 113 // Called only by unit tests. | 107 // Called only by unit tests. |
| 114 InMemoryURLIndex::InMemoryURLIndex() | 108 InMemoryURLIndex::InMemoryURLIndex(const FilePath& history_dir, |
| 109 const std::string& languages) |
| 115 : profile_(NULL), | 110 : profile_(NULL), |
| 116 private_data_(new URLIndexPrivateData), | 111 languages_(languages), |
| 117 restore_cache_observer_(NULL), | 112 history_dir_(history_dir), |
| 118 save_cache_observer_(NULL), | 113 index_available_(false), |
| 119 shutdown_(false), | 114 shutdown_(false), |
| 120 needs_to_be_cached_(false) { | 115 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
| 121 InitializeSchemeWhitelist(&scheme_whitelist_); | |
| 122 } | 116 } |
| 123 | 117 |
| 124 InMemoryURLIndex::~InMemoryURLIndex() { | 118 InMemoryURLIndex::~InMemoryURLIndex() {} |
| 125 // If there was a history directory (which there won't be for some unit tests) | 119 |
| 126 // then insure that the cache has already been saved. | 120 void InMemoryURLIndex::Init(bool disable_cache) { |
| 127 DCHECK(history_dir_.empty() || !needs_to_be_cached_); | 121 if (disable_cache) { |
| 122 RebuildFromHistoryIfLoaded(); |
| 123 } else { |
| 124 // It's safe to initialize the private data and the cache database without |
| 125 // using the sequenced worker pool as no other database operations will be |
| 126 // going on at the same time. |
| 127 BrowserThread::PostTaskAndReplyWithResult<bool>( |
| 128 BrowserThread::DB, FROM_HERE, |
| 129 base::Bind(&URLIndexPrivateData::Init, private_data_, sequence_token_), |
| 130 base::Bind(&InMemoryURLIndex::OnPrivateDataInitDone, |
| 131 weak_ptr_factory_.GetWeakPtr())); |
| 132 } |
| 128 } | 133 } |
| 129 | 134 |
| 130 void InMemoryURLIndex::Init() { | 135 void InMemoryURLIndex::OnPrivateDataInitDone(bool succeeded) { |
| 131 PostRestoreFromCacheFileTask(); | 136 if (shutdown_) |
| 137 return; |
| 138 if (succeeded) |
| 139 PostRestoreFromCacheTask(); |
| 140 else |
| 141 RebuildFromHistoryIfLoaded(); |
| 132 } | 142 } |
| 133 | 143 |
| 134 void InMemoryURLIndex::ShutDown() { | 144 void InMemoryURLIndex::Shutdown() { |
| 145 // Close down the cache database as quickly as possible. Any pending cache DB |
| 146 // transactions will detect that the database is no longer there and give up. |
| 135 registrar_.RemoveAll(); | 147 registrar_.RemoveAll(); |
| 136 cache_reader_consumer_.CancelAllRequests(); | 148 cache_reader_consumer_.CancelAllRequests(); |
| 137 shutdown_ = true; | 149 shutdown_ = true; |
| 138 FilePath path; | 150 private_data_->Shutdown(); |
| 139 if (!GetCacheFilePath(&path)) | |
| 140 return; | |
| 141 scoped_refptr<RefCountedBool> succeeded(new RefCountedBool(false)); | |
| 142 URLIndexPrivateData::WritePrivateDataToCacheFileTask( | |
| 143 private_data_, path, succeeded); | |
| 144 needs_to_be_cached_ = false; | |
| 145 } | 151 } |
| 146 | 152 |
| 147 void InMemoryURLIndex::ClearPrivateData() { | 153 void InMemoryURLIndex::AddObserver(InMemoryURLIndex::Observer* observer) { |
| 148 private_data_->Clear(); | 154 observers_.AddObserver(observer); |
| 149 } | 155 } |
| 150 | 156 |
| 151 bool InMemoryURLIndex::GetCacheFilePath(FilePath* file_path) { | 157 void InMemoryURLIndex::RemoveObserver(InMemoryURLIndex::Observer* observer) { |
| 152 if (history_dir_.empty()) | 158 observers_.RemoveObserver(observer); |
| 153 return false; | |
| 154 *file_path = history_dir_.Append(FILE_PATH_LITERAL("History Provider Cache")); | |
| 155 return true; | |
| 156 } | 159 } |
| 157 | 160 |
| 158 // Querying -------------------------------------------------------------------- | 161 // Querying -------------------------------------------------------------------- |
| 159 | 162 |
| 160 ScoredHistoryMatches InMemoryURLIndex::HistoryItemsForTerms( | 163 ScoredHistoryMatches InMemoryURLIndex::HistoryItemsForTerms( |
| 161 const string16& term_string) { | 164 const string16& term_string) { |
| 162 return private_data_->HistoryItemsForTerms(term_string); | 165 return private_data_->HistoryItemsForTerms(term_string); |
| 163 } | 166 } |
| 164 | 167 |
| 165 // Updating -------------------------------------------------------------------- | 168 // Updating -------------------------------------------------------------------- |
| 166 | 169 |
| 170 void InMemoryURLIndex::DeleteURL(const GURL& url) { |
| 171 private_data_->DeleteURL(url); |
| 172 } |
| 173 |
| 167 void InMemoryURLIndex::Observe(int notification_type, | 174 void InMemoryURLIndex::Observe(int notification_type, |
| 168 const content::NotificationSource& source, | 175 const content::NotificationSource& source, |
| 169 const content::NotificationDetails& details) { | 176 const content::NotificationDetails& details) { |
| 170 switch (notification_type) { | 177 switch (notification_type) { |
| 171 case chrome::NOTIFICATION_HISTORY_URL_VISITED: | 178 case chrome::NOTIFICATION_HISTORY_URL_VISITED: |
| 172 OnURLVisited(content::Details<URLVisitedDetails>(details).ptr()); | 179 OnURLVisited(content::Details<URLVisitedDetails>(details).ptr()); |
| 173 break; | 180 break; |
| 174 case chrome::NOTIFICATION_HISTORY_URLS_MODIFIED: | 181 case chrome::NOTIFICATION_HISTORY_URLS_MODIFIED: |
| 175 OnURLsModified( | 182 OnURLsModified( |
| 176 content::Details<history::URLsModifiedDetails>(details).ptr()); | 183 content::Details<history::URLsModifiedDetails>(details).ptr()); |
| 177 break; | 184 break; |
| 178 case chrome::NOTIFICATION_HISTORY_URLS_DELETED: | 185 case chrome::NOTIFICATION_HISTORY_URLS_DELETED: |
| 179 OnURLsDeleted( | 186 OnURLsDeleted( |
| 180 content::Details<history::URLsDeletedDetails>(details).ptr()); | 187 content::Details<history::URLsDeletedDetails>(details).ptr()); |
| 181 break; | 188 break; |
| 182 case chrome::NOTIFICATION_HISTORY_LOADED: | 189 case chrome::NOTIFICATION_HISTORY_LOADED: |
| 183 registrar_.Remove(this, chrome::NOTIFICATION_HISTORY_LOADED, | 190 registrar_.Remove(this, chrome::NOTIFICATION_HISTORY_LOADED, |
| 184 content::Source<Profile>(profile_)); | 191 content::Source<Profile>(profile_)); |
| 185 ScheduleRebuildFromHistory(); | 192 ScheduleRebuildFromHistory(); |
| 186 break; | 193 break; |
| 194 case chrome::NOTIFICATION_IN_MEMORY_URL_CACHE_DATABASE_FAILURE: |
| 195 RepairCacheDatabase(); |
| 196 break; |
| 187 default: | 197 default: |
| 188 // For simplicity, the unit tests send us all notifications, even when | 198 // For simplicity, the unit tests send us all notifications, even when |
| 189 // we haven't registered for them, so don't assert here. | 199 // we haven't registered for them, so don't assert here. |
| 190 break; | 200 break; |
| 191 } | 201 } |
| 192 } | 202 } |
| 193 | 203 |
| 194 void InMemoryURLIndex::OnURLVisited(const URLVisitedDetails* details) { | 204 void InMemoryURLIndex::OnURLVisited(const URLVisitedDetails* details) { |
| 195 needs_to_be_cached_ |= | 205 if (index_available_) |
| 196 private_data_->UpdateURL(details->row, languages_, scheme_whitelist_); | 206 private_data_->UpdateURL(details->row); |
| 207 else |
| 208 pending_updates_.push_back(IndexUpdateItem(UPDATE_VISIT, details->row)); |
| 197 } | 209 } |
| 198 | 210 |
| 199 void InMemoryURLIndex::OnURLsModified(const URLsModifiedDetails* details) { | 211 void InMemoryURLIndex::OnURLsModified(const URLsModifiedDetails* details) { |
| 200 for (URLRows::const_iterator row = details->changed_urls.begin(); | 212 for (URLRows::const_iterator row = details->changed_urls.begin(); |
| 201 row != details->changed_urls.end(); ++row) | 213 row != details->changed_urls.end(); ++row) { |
| 202 needs_to_be_cached_ |= | 214 if (index_available_) |
| 203 private_data_->UpdateURL(*row, languages_, scheme_whitelist_); | 215 private_data_->UpdateURL(*row); |
| 216 else |
| 217 pending_updates_.push_back(IndexUpdateItem(UPDATE_VISIT, *row)); |
| 218 } |
| 204 } | 219 } |
| 205 | 220 |
| 206 void InMemoryURLIndex::OnURLsDeleted(const URLsDeletedDetails* details) { | 221 void InMemoryURLIndex::OnURLsDeleted(const URLsDeletedDetails* details) { |
| 207 if (details->all_history) { | 222 if (details->all_history) { |
| 208 ClearPrivateData(); | 223 PostResetPrivateDataTask(); |
| 209 needs_to_be_cached_ = true; | |
| 210 } else { | 224 } else { |
| 211 for (URLRows::const_iterator row = details->rows.begin(); | 225 for (URLRows::const_iterator row = details->rows.begin(); |
| 212 row != details->rows.end(); ++row) | 226 row != details->rows.end(); ++row) { |
| 213 needs_to_be_cached_ |= private_data_->DeleteURL(row->url()); | 227 if (index_available_) |
| 228 DeleteURL(row->url()); |
| 229 else |
| 230 pending_updates_.push_back(IndexUpdateItem(DELETE_VISIT, *row)); |
| 231 } |
| 214 } | 232 } |
| 215 } | 233 } |
| 216 | 234 |
| 235 void InMemoryURLIndex::FlushPendingUpdates() { |
| 236 for (PendingUpdates::iterator i = pending_updates_.begin(); |
| 237 i != pending_updates_.end(); ++i) { |
| 238 if (i->update_type == UPDATE_VISIT) |
| 239 private_data_->UpdateURL(i->row); |
| 240 else if (i->update_type == DELETE_VISIT) |
| 241 DeleteURL(i->row.url()); |
| 242 } |
| 243 pending_updates_.clear(); |
| 244 } |
| 245 |
| 217 // Restoring from Cache -------------------------------------------------------- | 246 // Restoring from Cache -------------------------------------------------------- |
| 218 | 247 |
| 219 void InMemoryURLIndex::PostRestoreFromCacheFileTask() { | 248 void InMemoryURLIndex::PostRestoreFromCacheTask() { |
| 220 FilePath path; | 249 // It's safe to restore from the cache database without using the sequenced |
| 221 if (!GetCacheFilePath(&path) || shutdown_) | 250 // worker pool as no other database operations will be going on at the same |
| 222 return; | 251 // time. |
| 223 scoped_refptr<URLIndexPrivateData> restored_private_data = | 252 BrowserThread::PostTaskAndReplyWithResult<bool>( |
| 224 new URLIndexPrivateData; | 253 BrowserThread::DB, FROM_HERE, |
| 225 content::BrowserThread::PostTaskAndReply( | 254 base::Bind(&URLIndexPrivateData::RestoreFromCacheTask, private_data_), |
| 226 content::BrowserThread::FILE, FROM_HERE, | 255 base::Bind(&InMemoryURLIndex::OnCacheRestoreDone, |
| 227 base::Bind(&URLIndexPrivateData::RestoreFromFileTask, path, | 256 weak_ptr_factory_.GetWeakPtr())); |
| 228 restored_private_data, languages_), | |
| 229 base::Bind(&InMemoryURLIndex::OnCacheLoadDone, AsWeakPtr(), | |
| 230 restored_private_data)); | |
| 231 } | 257 } |
| 232 | 258 |
| 233 void InMemoryURLIndex::OnCacheLoadDone( | 259 void InMemoryURLIndex::OnCacheRestoreDone(bool succeeded) { |
| 234 scoped_refptr<URLIndexPrivateData> private_data) { | 260 if (shutdown_) { |
| 235 if (private_data.get() && !private_data->Empty()) { | 261 NotifyHasLoaded(); |
| 236 private_data_ = private_data; | 262 return; |
| 237 if (restore_cache_observer_) | 263 } |
| 238 restore_cache_observer_->OnCacheRestoreFinished(true); | 264 if (succeeded) { |
| 265 FlushPendingUpdates(); |
| 266 index_available_ = true; |
| 267 NotifyHasLoaded(); |
| 239 } else if (profile_) { | 268 } else if (profile_) { |
| 240 // When unable to restore from the cache file delete the cache file, if | 269 RebuildFromHistoryIfLoaded(); |
| 241 // it exists, and then rebuild from the history database if it's available, | |
| 242 // otherwise wait until the history database loaded and then rebuild. | |
| 243 FilePath path; | |
| 244 if (!GetCacheFilePath(&path) || shutdown_) | |
| 245 return; | |
| 246 content::BrowserThread::PostBlockingPoolTask( | |
| 247 FROM_HERE, base::Bind(DeleteCacheFile, path)); | |
| 248 HistoryService* service = | |
| 249 HistoryServiceFactory::GetForProfileWithoutCreating(profile_); | |
| 250 if (service && service->backend_loaded()) { | |
| 251 ScheduleRebuildFromHistory(); | |
| 252 } else { | |
| 253 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_LOADED, | |
| 254 content::Source<Profile>(profile_)); | |
| 255 } | |
| 256 } | 270 } |
| 257 } | 271 } |
| 258 | 272 |
| 273 void InMemoryURLIndex::NotifyHasLoaded() { |
| 274 FOR_EACH_OBSERVER(InMemoryURLIndex::Observer, observers_, Loaded()); |
| 275 } |
| 276 |
| 259 // Restoring from the History DB ----------------------------------------------- | 277 // Restoring from the History DB ----------------------------------------------- |
| 260 | 278 |
| 279 void InMemoryURLIndex::RebuildFromHistoryIfLoaded() { |
| 280 // When unable to restore from the cache database, rebuild from the history |
| 281 // database, if it's available, otherwise wait until the history database |
| 282 // has loaded and then rebuild the index. |
| 283 HistoryService* service = |
| 284 HistoryServiceFactory::GetForProfileIfExists(profile_, |
| 285 Profile::EXPLICIT_ACCESS); |
| 286 if (service && service->backend_loaded()) { |
| 287 ScheduleRebuildFromHistory(); |
| 288 } else { |
| 289 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_LOADED, |
| 290 content::Source<Profile>(profile_)); |
| 291 } |
| 292 } |
| 293 |
| 261 void InMemoryURLIndex::ScheduleRebuildFromHistory() { | 294 void InMemoryURLIndex::ScheduleRebuildFromHistory() { |
| 295 // It's possible that we were waiting on history to finish loading when |
| 296 // the profile was told to shut down. |
| 297 if (shutdown_) |
| 298 return; |
| 299 // Reset availability here as this function is called directly by unit tests. |
| 300 index_available_ = false; |
| 262 HistoryService* service = | 301 HistoryService* service = |
| 263 HistoryServiceFactory::GetForProfile(profile_, | 302 HistoryServiceFactory::GetForProfile(profile_, |
| 264 Profile::EXPLICIT_ACCESS); | 303 Profile::EXPLICIT_ACCESS); |
| 304 // Do not update the cache database while rebuilding. |
| 305 private_data_->set_cache_enabled(false); |
| 265 service->ScheduleDBTask( | 306 service->ScheduleDBTask( |
| 266 new InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask( | 307 new InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask(this), |
| 267 this, languages_, scheme_whitelist_), | |
| 268 &cache_reader_consumer_); | 308 &cache_reader_consumer_); |
| 269 } | 309 } |
| 270 | 310 |
| 271 void InMemoryURLIndex::DoneRebuidingPrivateDataFromHistoryDB( | 311 void InMemoryURLIndex::DoneRebuidingPrivateDataFromHistoryDB( |
| 272 bool succeeded, | 312 bool succeeded, |
| 273 scoped_refptr<URLIndexPrivateData> private_data) { | 313 scoped_refptr<URLIndexPrivateData> private_data) { |
| 274 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 314 DCHECK(!BrowserThread::IsWellKnownThread(BrowserThread::UI) || |
| 315 BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 316 if (shutdown_) |
| 317 return; |
| 275 if (succeeded) { | 318 if (succeeded) { |
| 276 private_data_ = private_data; | 319 private_data_ = private_data; |
| 277 PostSaveToCacheFileTask(); // Cache the newly rebuilt index. | 320 private_data_->set_cache_enabled(true); |
| 321 PostRefreshCacheTask(); // Cache the newly rebuilt index. |
| 278 } else { | 322 } else { |
| 279 private_data_->Clear(); // Dump the old private data. | 323 private_data_->set_cache_enabled(true); |
| 280 // There is no need to do anything with the cache file as it was deleted | 324 PostResetPrivateDataTask(); |
| 281 // when the rebuild from the history operation was kicked off. | |
| 282 } | |
| 283 if (restore_cache_observer_) | |
| 284 restore_cache_observer_->OnCacheRestoreFinished(succeeded); | |
| 285 } | |
| 286 | |
| 287 void InMemoryURLIndex::RebuildFromHistory(HistoryDatabase* history_db) { | |
| 288 private_data_ = URLIndexPrivateData::RebuildFromHistory(history_db, | |
| 289 languages_, | |
| 290 scheme_whitelist_); | |
| 291 } | |
| 292 | |
| 293 // Saving to Cache ------------------------------------------------------------- | |
| 294 | |
| 295 void InMemoryURLIndex::PostSaveToCacheFileTask() { | |
| 296 FilePath path; | |
| 297 if (!GetCacheFilePath(&path)) | |
| 298 return; | |
| 299 // If there is anything in our private data then make a copy of it and tell | |
| 300 // it to save itself to a file. | |
| 301 if (private_data_.get() && !private_data_->Empty()) { | |
| 302 // Note that ownership of the copy of our private data is passed to the | |
| 303 // completion closure below. | |
| 304 scoped_refptr<URLIndexPrivateData> private_data_copy = | |
| 305 private_data_->Duplicate(); | |
| 306 scoped_refptr<RefCountedBool> succeeded(new RefCountedBool(false)); | |
| 307 content::BrowserThread::PostTaskAndReply( | |
| 308 content::BrowserThread::FILE, FROM_HERE, | |
| 309 base::Bind(&URLIndexPrivateData::WritePrivateDataToCacheFileTask, | |
| 310 private_data_copy, path, succeeded), | |
| 311 base::Bind(&InMemoryURLIndex::OnCacheSaveDone, AsWeakPtr(), succeeded)); | |
| 312 } else { | |
| 313 // If there is no data in our index then delete any existing cache file. | |
| 314 content::BrowserThread::PostBlockingPoolTask( | |
| 315 FROM_HERE, | |
| 316 base::Bind(DeleteCacheFile, path)); | |
| 317 } | 325 } |
| 318 } | 326 } |
| 319 | 327 |
| 320 void InMemoryURLIndex::OnCacheSaveDone( | 328 // Reset Cache ----------------------------------------------------------------- |
| 321 scoped_refptr<RefCountedBool> succeeded) { | 329 |
| 322 if (save_cache_observer_) | 330 void InMemoryURLIndex::PostResetPrivateDataTask() { |
| 323 save_cache_observer_->OnCacheSaveFinished(succeeded->value()); | 331 index_available_ = false; |
| 332 scoped_refptr<base::SequencedTaskRunner> runner = |
| 333 BrowserThread::GetBlockingPool()->GetSequencedTaskRunner(sequence_token_); |
| 334 DCHECK(runner.get()); |
| 335 runner->PostTaskAndReply(FROM_HERE, |
| 336 base::Bind(&URLIndexPrivateData::Reset, private_data_), |
| 337 base::Bind(&InMemoryURLIndex::OnCacheRefreshOrResetDone, |
| 338 weak_ptr_factory_.GetWeakPtr())); |
| 339 } |
| 340 |
| 341 // Refresh Cache --------------------------------------------------------------- |
| 342 |
| 343 void InMemoryURLIndex::PostRefreshCacheTask() { |
| 344 scoped_refptr<base::SequencedTaskRunner> runner = |
| 345 BrowserThread::GetBlockingPool()->GetSequencedTaskRunner(sequence_token_); |
| 346 DCHECK(runner.get()); |
| 347 runner->PostTaskAndReply(FROM_HERE, |
| 348 base::Bind(&URLIndexPrivateData::RefreshCacheTask, private_data_), |
| 349 base::Bind(&InMemoryURLIndex::OnCacheRefreshOrResetDone, |
| 350 weak_ptr_factory_.GetWeakPtr())); |
| 351 } |
| 352 |
| 353 void InMemoryURLIndex::OnCacheRefreshOrResetDone() { |
| 354 if (shutdown_) { |
| 355 NotifyHasLoaded(); |
| 356 return; |
| 357 } |
| 358 FlushPendingUpdates(); |
| 359 index_available_ = true; |
| 360 NotifyHasLoaded(); |
| 361 } |
| 362 |
| 363 // Repair Cache ---------------------------------------------------------------- |
| 364 |
| 365 void InMemoryURLIndex::RepairCacheDatabase() { |
| 366 // The database will disable itself when it detects an error so re-enable the |
| 367 // database and try to refresh it from scratch. If that fails then the |
| 368 // database will be left in a disabled state and will be rebuilt from the |
| 369 // history database the next time the profile is opened. |
| 370 private_data_->set_cache_enabled(true); |
| 371 PostRefreshCacheTask(); // Cache the newly rebuilt index. |
| 324 } | 372 } |
| 325 | 373 |
| 326 } // namespace history | 374 } // namespace history |
| OLD | NEW |