history.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 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// The history system runs on a background thread so that potentially slow
6// database operations don't delay the browser. This backend processing is
7// represented by HistoryBackend. The HistoryService's job is to dispatch to
8// that thread.
9//
10// Main thread                       History thread
11// -----------                       --------------
12// HistoryService <----------------> HistoryBackend
13//                                   -> HistoryDatabase
14//                                      -> SQLite connection to History
15//                                   -> ArchivedDatabase
16//                                      -> SQLite connection to Archived History
17//                                   -> TextDatabaseManager
18//                                      -> SQLite connection to one month's data
19//                                      -> SQLite connection to one month's data
20//                                      ...
21//                                   -> ThumbnailDatabase
22//                                      -> SQLite connection to Thumbnails
23//                                         (and favicons)
24
25#include "chrome/browser/history/history.h"
26
27#include "base/callback.h"
28#include "base/message_loop.h"
29#include "base/path_service.h"
30#include "base/ref_counted.h"
31#include "base/string_util.h"
32#include "base/task.h"
33#include "chrome/browser/autocomplete/history_url_provider.h"
34#include "chrome/browser/browser_list.h"
35#include "chrome/browser/browser_process.h"
36#include "chrome/browser/browser_thread.h"
37#include "chrome/browser/browser_window.h"
38#include "chrome/browser/history/download_create_info.h"
39#include "chrome/browser/history/history_backend.h"
40#include "chrome/browser/history/history_notifications.h"
41#include "chrome/browser/history/history_types.h"
42#include "chrome/browser/history/in_memory_database.h"
43#include "chrome/browser/history/in_memory_history_backend.h"
44#include "chrome/browser/history/top_sites.h"
45#include "chrome/browser/prefs/pref_service.h"
46#include "chrome/browser/profiles/profile.h"
47#include "chrome/browser/visitedlink/visitedlink_master.h"
48#include "chrome/common/chrome_constants.h"
49#include "chrome/common/notification_service.h"
50#include "chrome/common/pref_names.h"
51#include "chrome/common/thumbnail_score.h"
52#include "chrome/common/url_constants.h"
53#include "grit/chromium_strings.h"
54#include "grit/generated_resources.h"
55#include "third_party/skia/include/core/SkBitmap.h"
56
57using base::Time;
58using history::HistoryBackend;
59
60namespace {
61
62static const char* kHistoryThreadName = "Chrome_HistoryThread";
63
64}  // namespace
65
66// Sends messages from the backend to us on the main thread. This must be a
67// separate class from the history service so that it can hold a reference to
68// the history service (otherwise we would have to manually AddRef and
69// Release when the Backend has a reference to us).
70class HistoryService::BackendDelegate : public HistoryBackend::Delegate {
71 public:
72  explicit BackendDelegate(HistoryService* history_service)
73      : history_service_(history_service),
74        message_loop_(MessageLoop::current()) {
75  }
76
77  virtual void NotifyProfileError(int message_id) {
78    // Send the backend to the history service on the main thread.
79    message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
80        &HistoryService::NotifyProfileError, message_id));
81  }
82
83  virtual void SetInMemoryBackend(
84      history::InMemoryHistoryBackend* backend) {
85    // Send the backend to the history service on the main thread.
86    message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
87        &HistoryService::SetInMemoryBackend, backend));
88  }
89
90  virtual void BroadcastNotifications(NotificationType type,
91                                      history::HistoryDetails* details) {
92    // Send the notification on the history thread.
93    if (NotificationService::current()) {
94      Details<history::HistoryDetails> det(details);
95      NotificationService::current()->Notify(type,
96                                             NotificationService::AllSources(),
97                                             det);
98    }
99    // Send the notification to the history service on the main thread.
100    message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
101        &HistoryService::BroadcastNotifications, type, details));
102  }
103
104  virtual void DBLoaded() {
105    message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
106        &HistoryService::OnDBLoaded));
107  }
108
109  virtual void StartTopSitesMigration() {
110    message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
111        &HistoryService::StartTopSitesMigration));
112  }
113
114 private:
115  scoped_refptr<HistoryService> history_service_;
116  MessageLoop* message_loop_;
117};
118
119// static
120const history::StarID HistoryService::kBookmarkBarID = 1;
121
122// The history thread is intentionally not a BrowserThread because the
123// sync integration unit tests depend on being able to create more than one
124// history thread.
125HistoryService::HistoryService()
126    : thread_(new base::Thread(kHistoryThreadName)),
127      profile_(NULL),
128      backend_loaded_(false),
129      bookmark_service_(NULL),
130      no_db_(false),
131      needs_top_sites_migration_(false) {
132}
133
134HistoryService::HistoryService(Profile* profile)
135    : thread_(new base::Thread(kHistoryThreadName)),
136      profile_(profile),
137      backend_loaded_(false),
138      bookmark_service_(NULL),
139      no_db_(false),
140      needs_top_sites_migration_(false) {
141  DCHECK(profile_);
142  registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED,
143                 Source<Profile>(profile_));
144  registrar_.Add(this, NotificationType::TEMPLATE_URL_REMOVED,
145                 Source<Profile>(profile_));
146}
147
148HistoryService::~HistoryService() {
149  // Shutdown the backend. This does nothing if Cleanup was already invoked.
150  Cleanup();
151}
152
153bool HistoryService::BackendLoaded() {
154  // NOTE: We start the backend loading even though it completes asynchronously
155  // and thus won't affect the return value of this function.  This is because
156  // callers of this assume that if the backend isn't yet loaded it will be
157  // soon, so they will either listen for notifications or just retry this call
158  // later.  If we've purged the backend, we haven't necessarily restarted it
159  // loading by now, so we need to trigger the load in order to maintain that
160  // expectation.
161  LoadBackendIfNecessary();
162  return backend_loaded_;
163}
164
165void HistoryService::UnloadBackend() {
166  if (!history_backend_)
167    return;  // Already unloaded.
168
169  // Get rid of the in-memory backend.
170  in_memory_backend_.reset();
171
172  // The backend's destructor must run on the history thread since it is not
173  // threadsafe. So this thread must not be the last thread holding a reference
174  // to the backend, or a crash could happen.
175  //
176  // We have a reference to the history backend. There is also an extra
177  // reference held by our delegate installed in the backend, which
178  // HistoryBackend::Closing will release. This means if we scheduled a call
179  // to HistoryBackend::Closing and *then* released our backend reference, there
180  // will be a race between us and the backend's Closing function to see who is
181  // the last holder of a reference. If the backend thread's Closing manages to
182  // run before we release our backend refptr, the last reference will be held
183  // by this thread and the destructor will be called from here.
184  //
185  // Therefore, we create a task to run the Closing operation first. This holds
186  // a reference to the backend. Then we release our reference, then we schedule
187  // the task to run. After the task runs, it will delete its reference from
188  // the history thread, ensuring everything works properly.
189  Task* closing_task =
190      NewRunnableMethod(history_backend_.get(), &HistoryBackend::Closing);
191  history_backend_ = NULL;
192  ScheduleTask(PRIORITY_NORMAL, closing_task);
193}
194
195void HistoryService::Cleanup() {
196  if (!thread_) {
197    // We've already cleaned up.
198    return;
199  }
200
201  // Unload the backend.
202  UnloadBackend();
203
204  // Delete the thread, which joins with the background thread. We defensively
205  // NULL the pointer before deleting it in case somebody tries to use it
206  // during shutdown, but this shouldn't happen.
207  base::Thread* thread = thread_;
208  thread_ = NULL;
209  delete thread;
210}
211
212void HistoryService::NotifyRenderProcessHostDestruction(const void* host) {
213  ScheduleAndForget(PRIORITY_NORMAL,
214                    &HistoryBackend::NotifyRenderProcessHostDestruction, host);
215}
216
217history::URLDatabase* HistoryService::InMemoryDatabase() {
218  // NOTE: See comments in BackendLoaded() as to why we call
219  // LoadBackendIfNecessary() here even though it won't affect the return value
220  // for this call.
221  LoadBackendIfNecessary();
222  if (in_memory_backend_.get())
223    return in_memory_backend_->db();
224  return NULL;
225}
226
227history::InMemoryURLIndex* HistoryService::InMemoryIndex() {
228  // NOTE: See comments in BackendLoaded() as to why we call
229  // LoadBackendIfNecessary() here even though it won't affect the return value
230  // for this call.
231  LoadBackendIfNecessary();
232  if (in_memory_backend_.get())
233    return in_memory_backend_->InMemoryIndex();
234  return NULL;
235}
236
237void HistoryService::SetSegmentPresentationIndex(int64 segment_id, int index) {
238  ScheduleAndForget(PRIORITY_UI,
239                    &HistoryBackend::SetSegmentPresentationIndex,
240                    segment_id, index);
241}
242
243void HistoryService::SetKeywordSearchTermsForURL(const GURL& url,
244                                                 TemplateURLID keyword_id,
245                                                 const string16& term) {
246  ScheduleAndForget(PRIORITY_UI,
247                    &HistoryBackend::SetKeywordSearchTermsForURL,
248                    url, keyword_id, term);
249}
250
251void HistoryService::DeleteAllSearchTermsForKeyword(
252    TemplateURLID keyword_id) {
253  ScheduleAndForget(PRIORITY_UI,
254                    &HistoryBackend::DeleteAllSearchTermsForKeyword,
255                    keyword_id);
256}
257
258HistoryService::Handle HistoryService::GetMostRecentKeywordSearchTerms(
259    TemplateURLID keyword_id,
260    const string16& prefix,
261    int max_count,
262    CancelableRequestConsumerBase* consumer,
263    GetMostRecentKeywordSearchTermsCallback* callback) {
264  return Schedule(PRIORITY_UI, &HistoryBackend::GetMostRecentKeywordSearchTerms,
265                  consumer,
266                  new history::GetMostRecentKeywordSearchTermsRequest(callback),
267                  keyword_id, prefix, max_count);
268}
269
270void HistoryService::URLsNoLongerBookmarked(const std::set<GURL>& urls) {
271  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::URLsNoLongerBookmarked,
272                    urls);
273}
274
275HistoryService::Handle HistoryService::ScheduleDBTask(
276    HistoryDBTask* task,
277    CancelableRequestConsumerBase* consumer) {
278  history::HistoryDBTaskRequest* request = new history::HistoryDBTaskRequest(
279      NewCallback(task, &HistoryDBTask::DoneRunOnMainThread));
280  request->value = task;  // The value is the task to execute.
281  return Schedule(PRIORITY_UI, &HistoryBackend::ProcessDBTask, consumer,
282                  request);
283}
284
285HistoryService::Handle HistoryService::QuerySegmentUsageSince(
286    CancelableRequestConsumerBase* consumer,
287    const Time from_time,
288    int max_result_count,
289    SegmentQueryCallback* callback) {
290  return Schedule(PRIORITY_UI, &HistoryBackend::QuerySegmentUsage,
291                  consumer, new history::QuerySegmentUsageRequest(callback),
292                  from_time, max_result_count);
293}
294
295void HistoryService::SetOnBackendDestroyTask(Task* task) {
296  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetOnBackendDestroyTask,
297                    MessageLoop::current(), task);
298}
299
300void HistoryService::AddPage(const GURL& url,
301                             const void* id_scope,
302                             int32 page_id,
303                             const GURL& referrer,
304                             PageTransition::Type transition,
305                             const history::RedirectList& redirects,
306                             history::VisitSource visit_source,
307                             bool did_replace_entry) {
308  AddPage(url, Time::Now(), id_scope, page_id, referrer, transition, redirects,
309          visit_source, did_replace_entry);
310}
311
312void HistoryService::AddPage(const GURL& url,
313                             Time time,
314                             const void* id_scope,
315                             int32 page_id,
316                             const GURL& referrer,
317                             PageTransition::Type transition,
318                             const history::RedirectList& redirects,
319                             history::VisitSource visit_source,
320                             bool did_replace_entry) {
321  scoped_refptr<history::HistoryAddPageArgs> request(
322      new history::HistoryAddPageArgs(url, time, id_scope, page_id, referrer,
323                                      redirects, transition, visit_source,
324                                      did_replace_entry));
325  AddPage(*request);
326}
327
328void HistoryService::AddPage(const history::HistoryAddPageArgs& add_page_args) {
329  DCHECK(thread_) << "History service being called after cleanup";
330
331  // Filter out unwanted URLs. We don't add auto-subframe URLs. They are a
332  // large part of history (think iframes for ads) and we never display them in
333  // history UI. We will still add manual subframes, which are ones the user
334  // has clicked on to get.
335  if (!CanAddURL(add_page_args.url))
336    return;
337
338  // Add link & all redirects to visited link list.
339  VisitedLinkMaster* visited_links;
340  if (profile_ && (visited_links = profile_->GetVisitedLinkMaster())) {
341    visited_links->AddURL(add_page_args.url);
342
343    if (!add_page_args.redirects.empty()) {
344      // We should not be asked to add a page in the middle of a redirect chain.
345      DCHECK_EQ(add_page_args.url,
346                add_page_args.redirects[add_page_args.redirects.size() - 1]);
347
348      // We need the !redirects.empty() condition above since size_t is unsigned
349      // and will wrap around when we subtract one from a 0 size.
350      for (size_t i = 0; i < add_page_args.redirects.size() - 1; i++)
351        visited_links->AddURL(add_page_args.redirects[i]);
352    }
353  }
354
355  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::AddPage,
356                    scoped_refptr<history::HistoryAddPageArgs>(
357                        add_page_args.Clone()));
358}
359
360void HistoryService::AddPageNoVisitForBookmark(const GURL& url) {
361  if (!CanAddURL(url))
362    return;
363
364  ScheduleAndForget(PRIORITY_NORMAL,
365                    &HistoryBackend::AddPageNoVisitForBookmark, url);
366}
367
368void HistoryService::SetPageTitle(const GURL& url,
369                                  const string16& title) {
370  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetPageTitle, url, title);
371}
372
373void HistoryService::AddPageWithDetails(const GURL& url,
374                                        const string16& title,
375                                        int visit_count,
376                                        int typed_count,
377                                        Time last_visit,
378                                        bool hidden,
379                                        history::VisitSource visit_source) {
380  // Filter out unwanted URLs.
381  if (!CanAddURL(url))
382    return;
383
384  // Add to the visited links system.
385  VisitedLinkMaster* visited_links;
386  if (profile_ && (visited_links = profile_->GetVisitedLinkMaster()))
387    visited_links->AddURL(url);
388
389  history::URLRow row(url);
390  row.set_title(title);
391  row.set_visit_count(visit_count);
392  row.set_typed_count(typed_count);
393  row.set_last_visit(last_visit);
394  row.set_hidden(hidden);
395
396  std::vector<history::URLRow> rows;
397  rows.push_back(row);
398
399  ScheduleAndForget(PRIORITY_NORMAL,
400                    &HistoryBackend::AddPagesWithDetails, rows, visit_source);
401}
402
403void HistoryService::AddPagesWithDetails(
404    const std::vector<history::URLRow>& info,
405    history::VisitSource visit_source) {
406
407  // Add to the visited links system.
408  VisitedLinkMaster* visited_links;
409  if (profile_ && (visited_links = profile_->GetVisitedLinkMaster())) {
410    std::vector<GURL> urls;
411    urls.reserve(info.size());
412    for (std::vector<history::URLRow>::const_iterator i = info.begin();
413         i != info.end();
414         ++i)
415      urls.push_back(i->url());
416
417    visited_links->AddURLs(urls);
418  }
419
420  ScheduleAndForget(PRIORITY_NORMAL,
421                    &HistoryBackend::AddPagesWithDetails, info, visit_source);
422}
423
424void HistoryService::SetPageContents(const GURL& url,
425                                     const string16& contents) {
426  if (!CanAddURL(url))
427    return;
428
429  ScheduleAndForget(PRIORITY_LOW, &HistoryBackend::SetPageContents,
430                    url, contents);
431}
432
433void HistoryService::SetPageThumbnail(const GURL& page_url,
434                                      const SkBitmap& thumbnail,
435                                      const ThumbnailScore& score) {
436  if (!CanAddURL(page_url))
437    return;
438
439  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetPageThumbnail,
440                    page_url, thumbnail, score);
441}
442
443HistoryService::Handle HistoryService::GetPageThumbnail(
444    const GURL& page_url,
445    CancelableRequestConsumerBase* consumer,
446    ThumbnailDataCallback* callback) {
447  return Schedule(PRIORITY_NORMAL, &HistoryBackend::GetPageThumbnail, consumer,
448                  new history::GetPageThumbnailRequest(callback), page_url);
449}
450
451void HistoryService::GetFavicon(FaviconService::GetFaviconRequest* request,
452                                const GURL& icon_url) {
453  Schedule(PRIORITY_NORMAL, &HistoryBackend::GetFavIcon, NULL, request,
454           icon_url);
455}
456
457void HistoryService::UpdateFaviconMappingAndFetch(
458    FaviconService::GetFaviconRequest* request,
459    const GURL& page_url,
460    const GURL& icon_url) {
461  Schedule(PRIORITY_NORMAL, &HistoryBackend::UpdateFavIconMappingAndFetch, NULL,
462           request, page_url, icon_url);
463}
464
465void HistoryService::GetFaviconForURL(
466    FaviconService::GetFaviconRequest* request,
467    const GURL& page_url) {
468  Schedule(PRIORITY_NORMAL, &HistoryBackend::GetFavIconForURL, NULL, request,
469           page_url);
470}
471
472void HistoryService::SetFavicon(const GURL& page_url,
473                                const GURL& icon_url,
474                                const std::vector<unsigned char>& image_data) {
475  if (!CanAddURL(page_url))
476    return;
477
478  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetFavIcon,
479      page_url, icon_url,
480      scoped_refptr<RefCountedMemory>(new RefCountedBytes(image_data)));
481}
482
483void HistoryService::SetFaviconOutOfDateForPage(const GURL& page_url) {
484  ScheduleAndForget(PRIORITY_NORMAL,
485                    &HistoryBackend::SetFavIconOutOfDateForPage, page_url);
486}
487
488void HistoryService::SetImportedFavicons(
489    const std::vector<history::ImportedFavIconUsage>& favicon_usage) {
490  ScheduleAndForget(PRIORITY_NORMAL,
491                    &HistoryBackend::SetImportedFavicons, favicon_usage);
492}
493
494void HistoryService::IterateURLs(URLEnumerator* enumerator) {
495  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::IterateURLs, enumerator);
496}
497
498HistoryService::Handle HistoryService::QueryURL(
499    const GURL& url,
500    bool want_visits,
501    CancelableRequestConsumerBase* consumer,
502    QueryURLCallback* callback) {
503  return Schedule(PRIORITY_UI, &HistoryBackend::QueryURL, consumer,
504                  new history::QueryURLRequest(callback), url, want_visits);
505}
506
507// Downloads -------------------------------------------------------------------
508
509// Handle creation of a download by creating an entry in the history service's
510// 'downloads' table.
511HistoryService::Handle HistoryService::CreateDownload(
512    const DownloadCreateInfo& create_info,
513    CancelableRequestConsumerBase* consumer,
514    HistoryService::DownloadCreateCallback* callback) {
515  return Schedule(PRIORITY_NORMAL, &HistoryBackend::CreateDownload, consumer,
516                  new history::DownloadCreateRequest(callback), create_info);
517}
518
519// Handle queries for a list of all downloads in the history database's
520// 'downloads' table.
521HistoryService::Handle HistoryService::QueryDownloads(
522    CancelableRequestConsumerBase* consumer,
523    DownloadQueryCallback* callback) {
524  return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryDownloads, consumer,
525                  new history::DownloadQueryRequest(callback));
526}
527
528// Changes all IN_PROGRESS in the database entries to CANCELED.
529// IN_PROGRESS entries are the corrupted entries, not updated by next function
530// because of the crash or some other extremal exit.
531void HistoryService::CleanUpInProgressEntries() {
532  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::CleanUpInProgressEntries);
533}
534
535// Handle updates for a particular download. This is a 'fire and forget'
536// operation, so we don't need to be called back.
537void HistoryService::UpdateDownload(int64 received_bytes,
538                                    int32 state,
539                                    int64 db_handle) {
540  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateDownload,
541                    received_bytes, state, db_handle);
542}
543
544void HistoryService::UpdateDownloadPath(const FilePath& path,
545                                        int64 db_handle) {
546  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateDownloadPath,
547                    path, db_handle);
548}
549
550void HistoryService::RemoveDownload(int64 db_handle) {
551  ScheduleAndForget(PRIORITY_NORMAL,
552                    &HistoryBackend::RemoveDownload, db_handle);
553}
554
555void HistoryService::RemoveDownloadsBetween(Time remove_begin,
556                                            Time remove_end) {
557  ScheduleAndForget(PRIORITY_NORMAL,
558                    &HistoryBackend::RemoveDownloadsBetween,
559                    remove_begin,
560                    remove_end);
561}
562
563HistoryService::Handle HistoryService::QueryHistory(
564    const string16& text_query,
565    const history::QueryOptions& options,
566    CancelableRequestConsumerBase* consumer,
567    QueryHistoryCallback* callback) {
568  return Schedule(PRIORITY_UI, &HistoryBackend::QueryHistory, consumer,
569                  new history::QueryHistoryRequest(callback),
570                  text_query, options);
571}
572
573HistoryService::Handle HistoryService::QueryRedirectsFrom(
574    const GURL& from_url,
575    CancelableRequestConsumerBase* consumer,
576    QueryRedirectsCallback* callback) {
577  return Schedule(PRIORITY_UI, &HistoryBackend::QueryRedirectsFrom, consumer,
578      new history::QueryRedirectsRequest(callback), from_url);
579}
580
581HistoryService::Handle HistoryService::QueryRedirectsTo(
582    const GURL& to_url,
583    CancelableRequestConsumerBase* consumer,
584    QueryRedirectsCallback* callback) {
585  return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryRedirectsTo, consumer,
586      new history::QueryRedirectsRequest(callback), to_url);
587}
588
589HistoryService::Handle HistoryService::GetVisitCountToHost(
590    const GURL& url,
591    CancelableRequestConsumerBase* consumer,
592    GetVisitCountToHostCallback* callback) {
593  return Schedule(PRIORITY_UI, &HistoryBackend::GetVisitCountToHost, consumer,
594      new history::GetVisitCountToHostRequest(callback), url);
595}
596
597HistoryService::Handle HistoryService::QueryTopURLsAndRedirects(
598    int result_count,
599    CancelableRequestConsumerBase* consumer,
600    QueryTopURLsAndRedirectsCallback* callback) {
601  return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryTopURLsAndRedirects,
602      consumer, new history::QueryTopURLsAndRedirectsRequest(callback),
603      result_count);
604}
605
606HistoryService::Handle HistoryService::QueryMostVisitedURLs(
607    int result_count,
608    int days_back,
609    CancelableRequestConsumerBase* consumer,
610    QueryMostVisitedURLsCallback* callback) {
611  return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryMostVisitedURLs,
612                  consumer,
613                  new history::QueryMostVisitedURLsRequest(callback),
614                  result_count, days_back);
615}
616
617void HistoryService::Observe(NotificationType type,
618                             const NotificationSource& source,
619                             const NotificationDetails& details) {
620  if (!thread_)
621    return;
622
623  switch (type.value) {
624    case NotificationType::HISTORY_URLS_DELETED: {
625      // Update the visited link system for deleted URLs. We will update the
626      // visited link system for added URLs as soon as we get the add
627      // notification (we don't have to wait for the backend, which allows us to
628      // be faster to update the state).
629      //
630      // For deleted URLs, we don't typically know what will be deleted since
631      // delete notifications are by time. We would also like to be more
632      // respectful of privacy and never tell the user something is gone when it
633      // isn't. Therefore, we update the delete URLs after the fact.
634      if (!profile_)
635        return;  // No profile, probably unit testing.
636      Details<history::URLsDeletedDetails> deleted_details(details);
637      VisitedLinkMaster* visited_links = profile_->GetVisitedLinkMaster();
638      if (!visited_links)
639        return;  // Nobody to update.
640      if (deleted_details->all_history)
641        visited_links->DeleteAllURLs();
642      else  // Delete individual ones.
643        visited_links->DeleteURLs(deleted_details->urls);
644      break;
645    }
646
647    case NotificationType::TEMPLATE_URL_REMOVED:
648      DeleteAllSearchTermsForKeyword(*(Details<TemplateURLID>(details).ptr()));
649      break;
650
651    default:
652      NOTREACHED();
653  }
654}
655
656bool HistoryService::Init(const FilePath& history_dir,
657                          BookmarkService* bookmark_service,
658                          bool no_db) {
659  if (!thread_->Start()) {
660    Cleanup();
661    return false;
662  }
663
664  history_dir_ = history_dir;
665  bookmark_service_ = bookmark_service;
666  no_db_ = no_db;
667
668  // Create the history backend.
669  LoadBackendIfNecessary();
670  return true;
671}
672
673void HistoryService::ScheduleAutocomplete(HistoryURLProvider* provider,
674                                          HistoryURLProviderParams* params) {
675  ScheduleAndForget(PRIORITY_UI, &HistoryBackend::ScheduleAutocomplete,
676                    scoped_refptr<HistoryURLProvider>(provider), params);
677}
678
679void HistoryService::ScheduleTask(SchedulePriority priority,
680                                  Task* task) {
681  // TODO(brettw): do prioritization.
682  thread_->message_loop()->PostTask(FROM_HERE, task);
683}
684
685// static
686bool HistoryService::CanAddURL(const GURL& url) {
687  if (!url.is_valid())
688    return false;
689
690  // TODO: We should allow kChromeUIScheme URLs if they have been explicitly
691  // typed.  Right now, however, these are marked as typed even when triggered
692  // by a shortcut or menu action.
693  if (url.SchemeIs(chrome::kJavaScriptScheme) ||
694      url.SchemeIs(chrome::kChromeDevToolsScheme) ||
695      url.SchemeIs(chrome::kChromeUIScheme) ||
696      url.SchemeIs(chrome::kViewSourceScheme) ||
697      url.SchemeIs(chrome::kChromeInternalScheme))
698    return false;
699
700  if (url.SchemeIs(chrome::kAboutScheme)) {
701    if (LowerCaseEqualsASCII(url.path(), "blank"))
702      return false;
703    // We allow all other about URLs since the user may like to see things
704    // like "about:memory" or "about:histograms" in their history and
705    // autocomplete.
706  }
707
708  return true;
709}
710
711void HistoryService::SetInMemoryBackend(
712    history::InMemoryHistoryBackend* mem_backend) {
713  DCHECK(!in_memory_backend_.get()) << "Setting mem DB twice";
714  in_memory_backend_.reset(mem_backend);
715
716  // The database requires additional initialization once we own it.
717  in_memory_backend_->AttachToHistoryService(profile_);
718}
719
720void HistoryService::NotifyProfileError(int message_id) {
721  Source<HistoryService> source(this);
722  NotificationService::current()->Notify(NotificationType::PROFILE_ERROR,
723                                         source, Details<int>(&message_id));
724}
725
726void HistoryService::DeleteURL(const GURL& url) {
727  // We will update the visited links when we observe the delete notifications.
728  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::DeleteURL, url);
729}
730
731void HistoryService::ExpireHistoryBetween(
732    const std::set<GURL>& restrict_urls,
733    Time begin_time, Time end_time,
734    CancelableRequestConsumerBase* consumer,
735    ExpireHistoryCallback* callback) {
736
737  // We will update the visited links when we observe the delete notifications.
738  Schedule(PRIORITY_UI, &HistoryBackend::ExpireHistoryBetween, consumer,
739           new history::ExpireHistoryRequest(callback),
740           restrict_urls, begin_time, end_time);
741}
742
743void HistoryService::BroadcastNotifications(
744    NotificationType type,
745    history::HistoryDetails* details_deleted) {
746  // We take ownership of the passed-in pointer and delete it. It was made for
747  // us on another thread, so the caller doesn't know when we will handle it.
748  scoped_ptr<history::HistoryDetails> details(details_deleted);
749  // TODO(evanm): this is currently necessitated by generate_profile, which
750  // runs without a browser process. generate_profile should really create
751  // a browser process, at which point this check can then be nuked.
752  if (!g_browser_process)
753    return;
754
755  if (!thread_)
756    return;
757
758  // The source of all of our notifications is the profile. Note that this
759  // pointer is NULL in unit tests.
760  Source<Profile> source(profile_);
761
762  // The details object just contains the pointer to the object that the
763  // backend has allocated for us. The receiver of the notification will cast
764  // this to the proper type.
765  Details<history::HistoryDetails> det(details_deleted);
766
767  NotificationService::current()->Notify(type, source, det);
768}
769
770void HistoryService::LoadBackendIfNecessary() {
771  if (!thread_ || history_backend_)
772    return;  // Failed to init, or already started loading.
773
774  scoped_refptr<HistoryBackend> backend(
775      new HistoryBackend(history_dir_,
776                         new BackendDelegate(this),
777                         bookmark_service_));
778  history_backend_.swap(backend);
779
780  // There may not be a profile when unit testing.
781  std::string languages;
782  if (profile_) {
783    PrefService* prefs = profile_->GetPrefs();
784    languages = prefs->GetString(prefs::kAcceptLanguages);
785  }
786  ScheduleAndForget(PRIORITY_UI, &HistoryBackend::Init, languages, no_db_);
787}
788
789void HistoryService::OnDBLoaded() {
790  backend_loaded_ = true;
791  NotificationService::current()->Notify(NotificationType::HISTORY_LOADED,
792                                         Source<Profile>(profile_),
793                                         Details<HistoryService>(this));
794  if (thread_ && profile_ && history::TopSites::IsEnabled()) {
795    // We don't want to force creation of TopSites.
796    history::TopSites* ts = profile_->GetTopSitesWithoutCreating();
797    if (ts)
798      ts->HistoryLoaded();
799  }
800}
801
802void HistoryService::StartTopSitesMigration() {
803  needs_top_sites_migration_ = true;
804  if (thread_ && profile_ && history::TopSites::IsEnabled()) {
805    // We don't want to force creation of TopSites.
806    history::TopSites* ts = profile_->GetTopSitesWithoutCreating();
807    if (ts)
808      ts->MigrateFromHistory();
809  }
810}
811
812void HistoryService::OnTopSitesReady() {
813  ScheduleAndForget(PRIORITY_NORMAL,
814                    &HistoryBackend::MigrateThumbnailsDatabase);
815}
816