| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/autocomplete/in_memory_url_index.h" | |
| 6 | |
| 7 #include "base/files/file_util.h" | |
| 8 #include "base/strings/utf_string_conversions.h" | |
| 9 #include "base/trace_event/trace_event.h" | |
| 10 #include "chrome/browser/autocomplete/url_index_private_data.h" | |
| 11 #include "components/history/core/browser/history_service.h" | |
| 12 #include "components/history/core/browser/url_database.h" | |
| 13 | |
| 14 using in_memory_url_index::InMemoryURLIndexCacheItem; | |
| 15 | |
| 16 // Initializes a whitelist of URL schemes. | |
| 17 void InitializeSchemeWhitelist( | |
| 18 std::set<std::string>* whitelist, | |
| 19 const std::set<std::string>& client_schemes_to_whitelist) { | |
| 20 DCHECK(whitelist); | |
| 21 if (!whitelist->empty()) | |
| 22 return; // Nothing to do, already initialized. | |
| 23 | |
| 24 whitelist->insert(client_schemes_to_whitelist.begin(), | |
| 25 client_schemes_to_whitelist.end()); | |
| 26 | |
| 27 whitelist->insert(std::string(url::kAboutScheme)); | |
| 28 whitelist->insert(std::string(url::kFileScheme)); | |
| 29 whitelist->insert(std::string(url::kFtpScheme)); | |
| 30 whitelist->insert(std::string(url::kHttpScheme)); | |
| 31 whitelist->insert(std::string(url::kHttpsScheme)); | |
| 32 whitelist->insert(std::string(url::kMailToScheme)); | |
| 33 } | |
| 34 | |
| 35 // Restore/SaveCacheObserver --------------------------------------------------- | |
| 36 | |
| 37 InMemoryURLIndex::RestoreCacheObserver::~RestoreCacheObserver() { | |
| 38 } | |
| 39 | |
| 40 InMemoryURLIndex::SaveCacheObserver::~SaveCacheObserver() { | |
| 41 } | |
| 42 | |
| 43 // RebuildPrivateDataFromHistoryDBTask ----------------------------------------- | |
| 44 | |
| 45 InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask:: | |
| 46 RebuildPrivateDataFromHistoryDBTask( | |
| 47 InMemoryURLIndex* index, | |
| 48 const std::string& languages, | |
| 49 const std::set<std::string>& scheme_whitelist) | |
| 50 : index_(index), | |
| 51 languages_(languages), | |
| 52 scheme_whitelist_(scheme_whitelist), | |
| 53 succeeded_(false) { | |
| 54 } | |
| 55 | |
| 56 bool InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask::RunOnDBThread( | |
| 57 history::HistoryBackend* backend, | |
| 58 history::HistoryDatabase* db) { | |
| 59 data_ = URLIndexPrivateData::RebuildFromHistory(db, languages_, | |
| 60 scheme_whitelist_); | |
| 61 succeeded_ = data_.get() && !data_->Empty(); | |
| 62 if (!succeeded_ && data_.get()) | |
| 63 data_->Clear(); | |
| 64 return true; | |
| 65 } | |
| 66 | |
| 67 void InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask:: | |
| 68 DoneRunOnMainThread() { | |
| 69 index_->DoneRebuidingPrivateDataFromHistoryDB(succeeded_, data_); | |
| 70 } | |
| 71 | |
| 72 InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask:: | |
| 73 ~RebuildPrivateDataFromHistoryDBTask() { | |
| 74 } | |
| 75 | |
| 76 // InMemoryURLIndex ------------------------------------------------------------ | |
| 77 | |
| 78 InMemoryURLIndex::InMemoryURLIndex( | |
| 79 bookmarks::BookmarkModel* bookmark_model, | |
| 80 history::HistoryService* history_service, | |
| 81 base::SequencedWorkerPool* worker_pool, | |
| 82 const base::FilePath& history_dir, | |
| 83 const std::string& languages, | |
| 84 const std::set<std::string>& client_schemes_to_whitelist) | |
| 85 : bookmark_model_(bookmark_model), | |
| 86 history_service_(history_service), | |
| 87 history_dir_(history_dir), | |
| 88 languages_(languages), | |
| 89 private_data_(new URLIndexPrivateData), | |
| 90 restore_cache_observer_(NULL), | |
| 91 save_cache_observer_(NULL), | |
| 92 task_runner_( | |
| 93 worker_pool->GetSequencedTaskRunner(worker_pool->GetSequenceToken())), | |
| 94 shutdown_(false), | |
| 95 restored_(false), | |
| 96 needs_to_be_cached_(false), | |
| 97 listen_to_history_service_loaded_(false) { | |
| 98 InitializeSchemeWhitelist(&scheme_whitelist_, client_schemes_to_whitelist); | |
| 99 // TODO(mrossetti): Register for language change notifications. | |
| 100 if (history_service_) | |
| 101 history_service_->AddObserver(this); | |
| 102 } | |
| 103 | |
| 104 InMemoryURLIndex::~InMemoryURLIndex() { | |
| 105 // If there was a history directory (which there won't be for some unit tests) | |
| 106 // then insure that the cache has already been saved. | |
| 107 DCHECK(history_dir_.empty() || !needs_to_be_cached_); | |
| 108 DCHECK(!history_service_); | |
| 109 DCHECK(shutdown_); | |
| 110 } | |
| 111 | |
| 112 void InMemoryURLIndex::Init() { | |
| 113 PostRestoreFromCacheFileTask(); | |
| 114 } | |
| 115 | |
| 116 void InMemoryURLIndex::ClearPrivateData() { | |
| 117 private_data_->Clear(); | |
| 118 } | |
| 119 | |
| 120 bool InMemoryURLIndex::GetCacheFilePath(base::FilePath* file_path) { | |
| 121 if (history_dir_.empty()) | |
| 122 return false; | |
| 123 *file_path = history_dir_.Append(FILE_PATH_LITERAL("History Provider Cache")); | |
| 124 return true; | |
| 125 } | |
| 126 | |
| 127 // Querying -------------------------------------------------------------------- | |
| 128 | |
| 129 ScoredHistoryMatches InMemoryURLIndex::HistoryItemsForTerms( | |
| 130 const base::string16& term_string, | |
| 131 size_t cursor_position, | |
| 132 size_t max_matches) { | |
| 133 return private_data_->HistoryItemsForTerms( | |
| 134 term_string, cursor_position, max_matches, languages_, bookmark_model_); | |
| 135 } | |
| 136 | |
| 137 // Updating -------------------------------------------------------------------- | |
| 138 | |
| 139 void InMemoryURLIndex::DeleteURL(const GURL& url) { | |
| 140 private_data_->DeleteURL(url); | |
| 141 } | |
| 142 | |
| 143 void InMemoryURLIndex::OnURLVisited(history::HistoryService* history_service, | |
| 144 ui::PageTransition transition, | |
| 145 const history::URLRow& row, | |
| 146 const history::RedirectList& redirects, | |
| 147 base::Time visit_time) { | |
| 148 DCHECK_EQ(history_service_, history_service); | |
| 149 needs_to_be_cached_ |= private_data_->UpdateURL(history_service_, | |
| 150 row, | |
| 151 languages_, | |
| 152 scheme_whitelist_, | |
| 153 &private_data_tracker_); | |
| 154 } | |
| 155 | |
| 156 void InMemoryURLIndex::OnURLsModified(history::HistoryService* history_service, | |
| 157 const history::URLRows& changed_urls) { | |
| 158 DCHECK_EQ(history_service_, history_service); | |
| 159 for (const auto& row : changed_urls) { | |
| 160 needs_to_be_cached_ |= private_data_->UpdateURL(history_service_, | |
| 161 row, | |
| 162 languages_, | |
| 163 scheme_whitelist_, | |
| 164 &private_data_tracker_); | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 void InMemoryURLIndex::OnURLsDeleted(history::HistoryService* history_service, | |
| 169 bool all_history, | |
| 170 bool expired, | |
| 171 const history::URLRows& deleted_rows, | |
| 172 const std::set<GURL>& favicon_urls) { | |
| 173 if (all_history) { | |
| 174 ClearPrivateData(); | |
| 175 needs_to_be_cached_ = true; | |
| 176 } else { | |
| 177 for (const auto& row : deleted_rows) | |
| 178 needs_to_be_cached_ |= private_data_->DeleteURL(row.url()); | |
| 179 } | |
| 180 // If we made changes, destroy the previous cache. Otherwise, if we go | |
| 181 // through an unclean shutdown (and therefore fail to write a new cache file), | |
| 182 // when Chrome restarts and we restore from the previous cache, we'll end up | |
| 183 // searching over URLs that may be deleted. This would be wrong, and | |
| 184 // surprising to the user who bothered to delete some URLs from his/her | |
| 185 // history. In this situation, deleting the cache is a better solution than | |
| 186 // writing a new cache (after deleting the URLs from the in-memory structure) | |
| 187 // because deleting the cache forces it to be rebuilt from history upon | |
| 188 // startup. If we instead write a new, updated cache then at the time of next | |
| 189 // startup (after an unclean shutdown) we will not rebuild the in-memory data | |
| 190 // structures from history but rather use the cache. This solution is | |
| 191 // mediocre because this cache may not have the most-recently-visited URLs | |
| 192 // in it (URLs visited after user deleted some URLs from history), which | |
| 193 // would be odd and confusing. It's better to force a rebuild. | |
| 194 base::FilePath path; | |
| 195 if (needs_to_be_cached_ && GetCacheFilePath(&path)) | |
| 196 task_runner_->PostTask( | |
| 197 FROM_HERE, | |
| 198 base::Bind(base::IgnoreResult(base::DeleteFile), path, false)); | |
| 199 } | |
| 200 | |
| 201 void InMemoryURLIndex::OnHistoryServiceLoaded( | |
| 202 history::HistoryService* history_service) { | |
| 203 if (listen_to_history_service_loaded_) | |
| 204 ScheduleRebuildFromHistory(); | |
| 205 listen_to_history_service_loaded_ = false; | |
| 206 } | |
| 207 | |
| 208 // Restoring from Cache -------------------------------------------------------- | |
| 209 | |
| 210 void InMemoryURLIndex::PostRestoreFromCacheFileTask() { | |
| 211 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 212 TRACE_EVENT0("browser", "InMemoryURLIndex::PostRestoreFromCacheFileTask"); | |
| 213 | |
| 214 base::FilePath path; | |
| 215 if (!GetCacheFilePath(&path) || shutdown_) { | |
| 216 restored_ = true; | |
| 217 if (restore_cache_observer_) | |
| 218 restore_cache_observer_->OnCacheRestoreFinished(false); | |
| 219 return; | |
| 220 } | |
| 221 | |
| 222 base::PostTaskAndReplyWithResult( | |
| 223 task_runner_.get(), | |
| 224 FROM_HERE, | |
| 225 base::Bind(&URLIndexPrivateData::RestoreFromFile, path, languages_), | |
| 226 base::Bind(&InMemoryURLIndex::OnCacheLoadDone, AsWeakPtr())); | |
| 227 } | |
| 228 | |
| 229 void InMemoryURLIndex::OnCacheLoadDone( | |
| 230 scoped_refptr<URLIndexPrivateData> private_data) { | |
| 231 if (private_data.get() && !private_data->Empty()) { | |
| 232 private_data_tracker_.TryCancelAll(); | |
| 233 private_data_ = private_data; | |
| 234 restored_ = true; | |
| 235 if (restore_cache_observer_) | |
| 236 restore_cache_observer_->OnCacheRestoreFinished(true); | |
| 237 } else if (history_service_) { | |
| 238 // When unable to restore from the cache file delete the cache file, if | |
| 239 // it exists, and then rebuild from the history database if it's available, | |
| 240 // otherwise wait until the history database loaded and then rebuild. | |
| 241 base::FilePath path; | |
| 242 if (!GetCacheFilePath(&path) || shutdown_) | |
| 243 return; | |
| 244 task_runner_->PostTask( | |
| 245 FROM_HERE, | |
| 246 base::Bind(base::IgnoreResult(base::DeleteFile), path, false)); | |
| 247 if (history_service_->backend_loaded()) { | |
| 248 ScheduleRebuildFromHistory(); | |
| 249 } else { | |
| 250 listen_to_history_service_loaded_ = true; | |
| 251 } | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 // Cleanup --------------------------------------------------------------------- | |
| 256 | |
| 257 void InMemoryURLIndex::Shutdown() { | |
| 258 if (history_service_) { | |
| 259 history_service_->RemoveObserver(this); | |
| 260 history_service_ = nullptr; | |
| 261 } | |
| 262 cache_reader_tracker_.TryCancelAll(); | |
| 263 shutdown_ = true; | |
| 264 base::FilePath path; | |
| 265 if (!GetCacheFilePath(&path)) | |
| 266 return; | |
| 267 private_data_tracker_.TryCancelAll(); | |
| 268 task_runner_->PostTask( | |
| 269 FROM_HERE, | |
| 270 base::Bind( | |
| 271 base::IgnoreResult( | |
| 272 &URLIndexPrivateData::WritePrivateDataToCacheFileTask), | |
| 273 private_data_, path)); | |
| 274 needs_to_be_cached_ = false; | |
| 275 } | |
| 276 | |
| 277 // Restoring from the History DB ----------------------------------------------- | |
| 278 | |
| 279 void InMemoryURLIndex::ScheduleRebuildFromHistory() { | |
| 280 DCHECK(history_service_); | |
| 281 history_service_->ScheduleDBTask( | |
| 282 scoped_ptr<history::HistoryDBTask>( | |
| 283 new InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask( | |
| 284 this, languages_, scheme_whitelist_)), | |
| 285 &cache_reader_tracker_); | |
| 286 } | |
| 287 | |
| 288 void InMemoryURLIndex::DoneRebuidingPrivateDataFromHistoryDB( | |
| 289 bool succeeded, | |
| 290 scoped_refptr<URLIndexPrivateData> private_data) { | |
| 291 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 292 if (succeeded) { | |
| 293 private_data_tracker_.TryCancelAll(); | |
| 294 private_data_ = private_data; | |
| 295 PostSaveToCacheFileTask(); // Cache the newly rebuilt index. | |
| 296 } else { | |
| 297 private_data_->Clear(); // Dump the old private data. | |
| 298 // There is no need to do anything with the cache file as it was deleted | |
| 299 // when the rebuild from the history operation was kicked off. | |
| 300 } | |
| 301 restored_ = true; | |
| 302 if (restore_cache_observer_) | |
| 303 restore_cache_observer_->OnCacheRestoreFinished(succeeded); | |
| 304 } | |
| 305 | |
| 306 void InMemoryURLIndex::RebuildFromHistory( | |
| 307 history::HistoryDatabase* history_db) { | |
| 308 private_data_tracker_.TryCancelAll(); | |
| 309 private_data_ = URLIndexPrivateData::RebuildFromHistory(history_db, | |
| 310 languages_, | |
| 311 scheme_whitelist_); | |
| 312 } | |
| 313 | |
| 314 // Saving to Cache ------------------------------------------------------------- | |
| 315 | |
| 316 void InMemoryURLIndex::PostSaveToCacheFileTask() { | |
| 317 base::FilePath path; | |
| 318 if (!GetCacheFilePath(&path)) | |
| 319 return; | |
| 320 // If there is anything in our private data then make a copy of it and tell | |
| 321 // it to save itself to a file. | |
| 322 if (private_data_.get() && !private_data_->Empty()) { | |
| 323 // Note that ownership of the copy of our private data is passed to the | |
| 324 // completion closure below. | |
| 325 scoped_refptr<URLIndexPrivateData> private_data_copy = | |
| 326 private_data_->Duplicate(); | |
| 327 base::PostTaskAndReplyWithResult( | |
| 328 task_runner_.get(), | |
| 329 FROM_HERE, | |
| 330 base::Bind(&URLIndexPrivateData::WritePrivateDataToCacheFileTask, | |
| 331 private_data_copy, path), | |
| 332 base::Bind(&InMemoryURLIndex::OnCacheSaveDone, AsWeakPtr())); | |
| 333 } else { | |
| 334 // If there is no data in our index then delete any existing cache file. | |
| 335 task_runner_->PostTask( | |
| 336 FROM_HERE, | |
| 337 base::Bind(base::IgnoreResult(base::DeleteFile), path, false)); | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 void InMemoryURLIndex::OnCacheSaveDone(bool succeeded) { | |
| 342 if (save_cache_observer_) | |
| 343 save_cache_observer_->OnCacheSaveFinished(succeeded); | |
| 344 } | |
| OLD | NEW |