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.
5 #include "chrome/browser/history/in_memory_url_index.h"
7 #include "base/debug/trace_event.h"
8 #include "base/file_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/history/history_notifications.h"
13 #include "chrome/browser/history/history_service.h"
14 #include "chrome/browser/history/history_service_factory.h"
15 #include "chrome/browser/history/url_database.h"
16 #include "chrome/browser/history/url_index_private_data.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/common/url_constants.h"
19 #include "components/bookmarks/browser/bookmark_model.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/notification_details.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/notification_source.h"
25 using in_memory_url_index::InMemoryURLIndexCacheItem;
29 // Called by DoSaveToCacheFile to delete any old cache file at |path| when
30 // there is no private data to save. Runs on the FILE thread.
31 void DeleteCacheFile(const base::FilePath& path) {
32 DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
33 base::DeleteFile(path, false);
36 // Initializes a whitelist of URL schemes.
37 void InitializeSchemeWhitelist(std::set<std::string>* whitelist) {
39 if (!whitelist->empty())
40 return; // Nothing to do, already initialized.
41 whitelist->insert(std::string(url::kAboutScheme));
42 whitelist->insert(std::string(content::kChromeUIScheme));
43 whitelist->insert(std::string(url::kFileScheme));
44 whitelist->insert(std::string(url::kFtpScheme));
45 whitelist->insert(std::string(url::kHttpScheme));
46 whitelist->insert(std::string(url::kHttpsScheme));
47 whitelist->insert(std::string(url::kMailToScheme));
50 // Restore/SaveCacheObserver ---------------------------------------------------
52 InMemoryURLIndex::RestoreCacheObserver::~RestoreCacheObserver() {}
54 InMemoryURLIndex::SaveCacheObserver::~SaveCacheObserver() {}
56 // RebuildPrivateDataFromHistoryDBTask -----------------------------------------
58 InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask::
59 RebuildPrivateDataFromHistoryDBTask(
60 InMemoryURLIndex* index,
61 const std::string& languages,
62 const std::set<std::string>& scheme_whitelist)
64 languages_(languages),
65 scheme_whitelist_(scheme_whitelist),
69 bool InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask::RunOnDBThread(
70 HistoryBackend* backend,
71 HistoryDatabase* db) {
72 data_ = URLIndexPrivateData::RebuildFromHistory(db, languages_,
74 succeeded_ = data_.get() && !data_->Empty();
75 if (!succeeded_ && data_.get())
80 void InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask::
81 DoneRunOnMainThread() {
82 index_->DoneRebuidingPrivateDataFromHistoryDB(succeeded_, data_);
85 InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask::
86 ~RebuildPrivateDataFromHistoryDBTask() {
89 // InMemoryURLIndex ------------------------------------------------------------
91 InMemoryURLIndex::InMemoryURLIndex(Profile* profile,
92 const base::FilePath& history_dir,
93 const std::string& languages,
94 HistoryClient* history_client)
96 history_client_(history_client),
97 history_dir_(history_dir),
98 languages_(languages),
99 private_data_(new URLIndexPrivateData),
100 restore_cache_observer_(NULL),
101 save_cache_observer_(NULL),
104 needs_to_be_cached_(false) {
105 InitializeSchemeWhitelist(&scheme_whitelist_);
107 // TODO(mrossetti): Register for language change notifications.
108 content::Source<Profile> source(profile);
109 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URL_VISITED, source);
110 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
112 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, source);
116 // Called only by unit tests.
117 InMemoryURLIndex::InMemoryURLIndex()
119 history_client_(NULL),
120 private_data_(new URLIndexPrivateData),
121 restore_cache_observer_(NULL),
122 save_cache_observer_(NULL),
125 needs_to_be_cached_(false) {
126 InitializeSchemeWhitelist(&scheme_whitelist_);
129 InMemoryURLIndex::~InMemoryURLIndex() {
130 // If there was a history directory (which there won't be for some unit tests)
131 // then insure that the cache has already been saved.
132 DCHECK(history_dir_.empty() || !needs_to_be_cached_);
135 void InMemoryURLIndex::Init() {
136 PostRestoreFromCacheFileTask();
139 void InMemoryURLIndex::ShutDown() {
140 registrar_.RemoveAll();
141 cache_reader_consumer_.CancelAllRequests();
144 if (!GetCacheFilePath(&path))
146 private_data_->CancelPendingUpdates();
147 URLIndexPrivateData::WritePrivateDataToCacheFileTask(private_data_, path);
148 needs_to_be_cached_ = false;
151 void InMemoryURLIndex::ClearPrivateData() {
152 private_data_->Clear();
155 bool InMemoryURLIndex::GetCacheFilePath(base::FilePath* file_path) {
156 if (history_dir_.empty())
158 *file_path = history_dir_.Append(FILE_PATH_LITERAL("History Provider Cache"));
162 // Querying --------------------------------------------------------------------
164 ScoredHistoryMatches InMemoryURLIndex::HistoryItemsForTerms(
165 const base::string16& term_string,
166 size_t cursor_position,
167 size_t max_matches) {
168 return private_data_->HistoryItemsForTerms(
176 // Updating --------------------------------------------------------------------
178 void InMemoryURLIndex::DeleteURL(const GURL& url) {
179 private_data_->DeleteURL(url);
182 void InMemoryURLIndex::Observe(int notification_type,
183 const content::NotificationSource& source,
184 const content::NotificationDetails& details) {
185 switch (notification_type) {
186 case chrome::NOTIFICATION_HISTORY_URL_VISITED:
187 OnURLVisited(content::Details<URLVisitedDetails>(details).ptr());
189 case chrome::NOTIFICATION_HISTORY_URLS_MODIFIED:
191 content::Details<history::URLsModifiedDetails>(details).ptr());
193 case chrome::NOTIFICATION_HISTORY_URLS_DELETED:
195 content::Details<history::URLsDeletedDetails>(details).ptr());
197 case chrome::NOTIFICATION_HISTORY_LOADED:
198 registrar_.Remove(this, chrome::NOTIFICATION_HISTORY_LOADED,
199 content::Source<Profile>(profile_));
200 ScheduleRebuildFromHistory();
203 // For simplicity, the unit tests send us all notifications, even when
204 // we haven't registered for them, so don't assert here.
209 void InMemoryURLIndex::OnURLVisited(const URLVisitedDetails* details) {
210 HistoryService* service =
211 HistoryServiceFactory::GetForProfile(profile_,
212 Profile::EXPLICIT_ACCESS);
213 needs_to_be_cached_ |= private_data_->UpdateURL(
214 service, details->row, languages_, scheme_whitelist_);
217 void InMemoryURLIndex::OnURLsModified(const URLsModifiedDetails* details) {
218 HistoryService* service =
219 HistoryServiceFactory::GetForProfile(profile_,
220 Profile::EXPLICIT_ACCESS);
221 for (URLRows::const_iterator row = details->changed_urls.begin();
222 row != details->changed_urls.end(); ++row)
223 needs_to_be_cached_ |=
224 private_data_->UpdateURL(service, *row, languages_, scheme_whitelist_);
227 void InMemoryURLIndex::OnURLsDeleted(const URLsDeletedDetails* details) {
228 if (details->all_history) {
230 needs_to_be_cached_ = true;
232 for (URLRows::const_iterator row = details->rows.begin();
233 row != details->rows.end(); ++row)
234 needs_to_be_cached_ |= private_data_->DeleteURL(row->url());
238 // Restoring from Cache --------------------------------------------------------
240 void InMemoryURLIndex::PostRestoreFromCacheFileTask() {
241 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
242 TRACE_EVENT0("browser", "InMemoryURLIndex::PostRestoreFromCacheFileTask");
245 if (!GetCacheFilePath(&path) || shutdown_) {
247 if (restore_cache_observer_)
248 restore_cache_observer_->OnCacheRestoreFinished(false);
252 content::BrowserThread::PostTaskAndReplyWithResult
253 <scoped_refptr<URLIndexPrivateData> >(
254 content::BrowserThread::FILE, FROM_HERE,
255 base::Bind(&URLIndexPrivateData::RestoreFromFile, path, languages_),
256 base::Bind(&InMemoryURLIndex::OnCacheLoadDone, AsWeakPtr()));
259 void InMemoryURLIndex::OnCacheLoadDone(
260 scoped_refptr<URLIndexPrivateData> private_data) {
261 if (private_data.get() && !private_data->Empty()) {
262 private_data_ = private_data;
264 if (restore_cache_observer_)
265 restore_cache_observer_->OnCacheRestoreFinished(true);
266 } else if (profile_) {
267 // When unable to restore from the cache file delete the cache file, if
268 // it exists, and then rebuild from the history database if it's available,
269 // otherwise wait until the history database loaded and then rebuild.
271 if (!GetCacheFilePath(&path) || shutdown_)
273 content::BrowserThread::PostBlockingPoolTask(
274 FROM_HERE, base::Bind(DeleteCacheFile, path));
275 HistoryService* service =
276 HistoryServiceFactory::GetForProfileWithoutCreating(profile_);
277 if (service && service->backend_loaded()) {
278 ScheduleRebuildFromHistory();
280 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_LOADED,
281 content::Source<Profile>(profile_));
286 // Restoring from the History DB -----------------------------------------------
288 void InMemoryURLIndex::ScheduleRebuildFromHistory() {
289 HistoryService* service =
290 HistoryServiceFactory::GetForProfile(profile_,
291 Profile::EXPLICIT_ACCESS);
292 service->ScheduleDBTask(
293 new InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask(
294 this, languages_, scheme_whitelist_),
295 &cache_reader_consumer_);
298 void InMemoryURLIndex::DoneRebuidingPrivateDataFromHistoryDB(
300 scoped_refptr<URLIndexPrivateData> private_data) {
301 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
303 private_data_ = private_data;
304 PostSaveToCacheFileTask(); // Cache the newly rebuilt index.
306 private_data_->Clear(); // Dump the old private data.
307 // There is no need to do anything with the cache file as it was deleted
308 // when the rebuild from the history operation was kicked off.
311 if (restore_cache_observer_)
312 restore_cache_observer_->OnCacheRestoreFinished(succeeded);
315 void InMemoryURLIndex::RebuildFromHistory(HistoryDatabase* history_db) {
316 private_data_ = URLIndexPrivateData::RebuildFromHistory(history_db,
321 // Saving to Cache -------------------------------------------------------------
323 void InMemoryURLIndex::PostSaveToCacheFileTask() {
325 if (!GetCacheFilePath(&path))
327 // If there is anything in our private data then make a copy of it and tell
328 // it to save itself to a file.
329 if (private_data_.get() && !private_data_->Empty()) {
330 // Note that ownership of the copy of our private data is passed to the
331 // completion closure below.
332 scoped_refptr<URLIndexPrivateData> private_data_copy =
333 private_data_->Duplicate();
334 content::BrowserThread::PostTaskAndReplyWithResult<bool>(
335 content::BrowserThread::FILE, FROM_HERE,
336 base::Bind(&URLIndexPrivateData::WritePrivateDataToCacheFileTask,
337 private_data_copy, path),
338 base::Bind(&InMemoryURLIndex::OnCacheSaveDone, AsWeakPtr()));
340 // If there is no data in our index then delete any existing cache file.
341 content::BrowserThread::PostBlockingPoolTask(
343 base::Bind(DeleteCacheFile, path));
347 void InMemoryURLIndex::OnCacheSaveDone(bool succeeded) {
348 if (save_cache_observer_)
349 save_cache_observer_->OnCacheSaveFinished(succeeded);
352 } // namespace history