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