38eb30ca4410d7cffb2d206c525c7d6f22d9f07f
[platform/framework/web/crosswalk.git] / src / chrome / browser / history / in_memory_url_index.cc
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/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"
24
25 using in_memory_url_index::InMemoryURLIndexCacheItem;
26
27 namespace history {
28
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);
34 }
35
36 // Initializes a whitelist of URL schemes.
37 void InitializeSchemeWhitelist(std::set<std::string>* whitelist) {
38   DCHECK(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));
48 }
49
50 // Restore/SaveCacheObserver ---------------------------------------------------
51
52 InMemoryURLIndex::RestoreCacheObserver::~RestoreCacheObserver() {}
53
54 InMemoryURLIndex::SaveCacheObserver::~SaveCacheObserver() {}
55
56 // RebuildPrivateDataFromHistoryDBTask -----------------------------------------
57
58 InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask::
59     RebuildPrivateDataFromHistoryDBTask(
60         InMemoryURLIndex* index,
61         const std::string& languages,
62         const std::set<std::string>& scheme_whitelist)
63     : index_(index),
64       languages_(languages),
65       scheme_whitelist_(scheme_whitelist),
66       succeeded_(false) {
67 }
68
69 bool InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask::RunOnDBThread(
70     HistoryBackend* backend,
71     HistoryDatabase* db) {
72   data_ = URLIndexPrivateData::RebuildFromHistory(db, languages_,
73                                                   scheme_whitelist_);
74   succeeded_ = data_.get() && !data_->Empty();
75   if (!succeeded_ && data_.get())
76     data_->Clear();
77   return true;
78 }
79
80 void InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask::
81     DoneRunOnMainThread() {
82   index_->DoneRebuidingPrivateDataFromHistoryDB(succeeded_, data_);
83 }
84
85 InMemoryURLIndex::RebuildPrivateDataFromHistoryDBTask::
86     ~RebuildPrivateDataFromHistoryDBTask() {
87 }
88
89 // InMemoryURLIndex ------------------------------------------------------------
90
91 InMemoryURLIndex::InMemoryURLIndex(Profile* profile,
92                                    const base::FilePath& history_dir,
93                                    const std::string& languages,
94                                    HistoryClient* history_client)
95     : profile_(profile),
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),
102       shutdown_(false),
103       restored_(false),
104       needs_to_be_cached_(false) {
105   InitializeSchemeWhitelist(&scheme_whitelist_);
106   if (profile) {
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,
111                    source);
112     registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, source);
113   }
114 }
115
116 // Called only by unit tests.
117 InMemoryURLIndex::InMemoryURLIndex()
118     : profile_(NULL),
119       history_client_(NULL),
120       private_data_(new URLIndexPrivateData),
121       restore_cache_observer_(NULL),
122       save_cache_observer_(NULL),
123       shutdown_(false),
124       restored_(false),
125       needs_to_be_cached_(false) {
126   InitializeSchemeWhitelist(&scheme_whitelist_);
127 }
128
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_);
133 }
134
135 void InMemoryURLIndex::Init() {
136   PostRestoreFromCacheFileTask();
137 }
138
139 void InMemoryURLIndex::ShutDown() {
140   registrar_.RemoveAll();
141   cache_reader_consumer_.CancelAllRequests();
142   shutdown_ = true;
143   base::FilePath path;
144   if (!GetCacheFilePath(&path))
145     return;
146   private_data_->CancelPendingUpdates();
147   URLIndexPrivateData::WritePrivateDataToCacheFileTask(private_data_, path);
148   needs_to_be_cached_ = false;
149 }
150
151 void InMemoryURLIndex::ClearPrivateData() {
152   private_data_->Clear();
153 }
154
155 bool InMemoryURLIndex::GetCacheFilePath(base::FilePath* file_path) {
156   if (history_dir_.empty())
157     return false;
158   *file_path = history_dir_.Append(FILE_PATH_LITERAL("History Provider Cache"));
159   return true;
160 }
161
162 // Querying --------------------------------------------------------------------
163
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(
169       term_string,
170       cursor_position,
171       max_matches,
172       languages_,
173       history_client_);
174 }
175
176 // Updating --------------------------------------------------------------------
177
178 void InMemoryURLIndex::DeleteURL(const GURL& url) {
179   private_data_->DeleteURL(url);
180 }
181
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());
188       break;
189     case chrome::NOTIFICATION_HISTORY_URLS_MODIFIED:
190       OnURLsModified(
191           content::Details<history::URLsModifiedDetails>(details).ptr());
192       break;
193     case chrome::NOTIFICATION_HISTORY_URLS_DELETED:
194       OnURLsDeleted(
195           content::Details<history::URLsDeletedDetails>(details).ptr());
196       break;
197     case chrome::NOTIFICATION_HISTORY_LOADED:
198       registrar_.Remove(this, chrome::NOTIFICATION_HISTORY_LOADED,
199                         content::Source<Profile>(profile_));
200       ScheduleRebuildFromHistory();
201       break;
202     default:
203       // For simplicity, the unit tests send us all notifications, even when
204       // we haven't registered for them, so don't assert here.
205       break;
206   }
207 }
208
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_);
215 }
216
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_);
225 }
226
227 void InMemoryURLIndex::OnURLsDeleted(const URLsDeletedDetails* details) {
228   if (details->all_history) {
229     ClearPrivateData();
230     needs_to_be_cached_ = true;
231   } else {
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());
235   }
236 }
237
238 // Restoring from Cache --------------------------------------------------------
239
240 void InMemoryURLIndex::PostRestoreFromCacheFileTask() {
241   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
242   TRACE_EVENT0("browser", "InMemoryURLIndex::PostRestoreFromCacheFileTask");
243
244   base::FilePath path;
245   if (!GetCacheFilePath(&path) || shutdown_) {
246     restored_ = true;
247     if (restore_cache_observer_)
248       restore_cache_observer_->OnCacheRestoreFinished(false);
249     return;
250   }
251
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()));
257 }
258
259 void InMemoryURLIndex::OnCacheLoadDone(
260     scoped_refptr<URLIndexPrivateData> private_data) {
261   if (private_data.get() && !private_data->Empty()) {
262     private_data_ = private_data;
263     restored_ = true;
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.
270     base::FilePath path;
271     if (!GetCacheFilePath(&path) || shutdown_)
272       return;
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();
279     } else {
280       registrar_.Add(this, chrome::NOTIFICATION_HISTORY_LOADED,
281                      content::Source<Profile>(profile_));
282     }
283   }
284 }
285
286 // Restoring from the History DB -----------------------------------------------
287
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_);
296 }
297
298 void InMemoryURLIndex::DoneRebuidingPrivateDataFromHistoryDB(
299     bool succeeded,
300     scoped_refptr<URLIndexPrivateData> private_data) {
301   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
302   if (succeeded) {
303     private_data_ = private_data;
304     PostSaveToCacheFileTask();  // Cache the newly rebuilt index.
305   } else {
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.
309   }
310   restored_ = true;
311   if (restore_cache_observer_)
312     restore_cache_observer_->OnCacheRestoreFinished(succeeded);
313 }
314
315 void InMemoryURLIndex::RebuildFromHistory(HistoryDatabase* history_db) {
316   private_data_ = URLIndexPrivateData::RebuildFromHistory(history_db,
317                                                           languages_,
318                                                           scheme_whitelist_);
319 }
320
321 // Saving to Cache -------------------------------------------------------------
322
323 void InMemoryURLIndex::PostSaveToCacheFileTask() {
324   base::FilePath path;
325   if (!GetCacheFilePath(&path))
326     return;
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()));
339   } else {
340     // If there is no data in our index then delete any existing cache file.
341     content::BrowserThread::PostBlockingPoolTask(
342         FROM_HERE,
343         base::Bind(DeleteCacheFile, path));
344   }
345 }
346
347 void InMemoryURLIndex::OnCacheSaveDone(bool succeeded) {
348   if (save_cache_observer_)
349     save_cache_observer_->OnCacheSaveFinished(succeeded);
350 }
351
352 }  // namespace history