history_service.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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// 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_service.h"
26
27#include "base/bind_helpers.h"
28#include "base/callback.h"
29#include "base/command_line.h"
30#include "base/compiler_specific.h"
31#include "base/location.h"
32#include "base/memory/ref_counted.h"
33#include "base/message_loop.h"
34#include "base/path_service.h"
35#include "base/prefs/pref_service.h"
36#include "base/thread_task_runner_handle.h"
37#include "base/threading/thread.h"
38#include "base/time/time.h"
39#include "chrome/browser/autocomplete/history_url_provider.h"
40#include "chrome/browser/bookmarks/bookmark_model.h"
41#include "chrome/browser/bookmarks/bookmark_model_factory.h"
42#include "chrome/browser/browser_process.h"
43#include "chrome/browser/chrome_notification_types.h"
44#include "chrome/browser/history/download_row.h"
45#include "chrome/browser/history/history_backend.h"
46#include "chrome/browser/history/history_notifications.h"
47#include "chrome/browser/history/history_types.h"
48#include "chrome/browser/history/in_memory_database.h"
49#include "chrome/browser/history/in_memory_history_backend.h"
50#include "chrome/browser/history/in_memory_url_index.h"
51#include "chrome/browser/history/top_sites.h"
52#include "chrome/browser/history/visit_database.h"
53#include "chrome/browser/history/visit_filter.h"
54#include "chrome/browser/history/web_history_service.h"
55#include "chrome/browser/history/web_history_service_factory.h"
56#include "chrome/browser/profiles/profile.h"
57#include "chrome/browser/ui/profile_error_dialog.h"
58#include "chrome/common/chrome_constants.h"
59#include "chrome/common/chrome_switches.h"
60#include "chrome/common/importer/imported_favicon_usage.h"
61#include "chrome/common/pref_names.h"
62#include "chrome/common/thumbnail_score.h"
63#include "chrome/common/url_constants.h"
64#include "components/visitedlink/browser/visitedlink_master.h"
65#include "content/public/browser/browser_thread.h"
66#include "content/public/browser/download_item.h"
67#include "content/public/browser/notification_service.h"
68#include "grit/chromium_strings.h"
69#include "grit/generated_resources.h"
70#include "sync/api/sync_error_factory.h"
71#include "third_party/skia/include/core/SkBitmap.h"
72
73using base::Time;
74using history::HistoryBackend;
75
76namespace {
77
78static const char* kHistoryThreadName = "Chrome_HistoryThread";
79
80template<typename PODType> void DerefPODType(
81    const base::Callback<void(PODType)>& callback, PODType* pod_value) {
82  callback.Run(*pod_value);
83}
84
85void RunWithFaviconResults(
86    const FaviconService::FaviconResultsCallback& callback,
87    std::vector<chrome::FaviconBitmapResult>* bitmap_results) {
88  callback.Run(*bitmap_results);
89}
90
91// Extract history::URLRows into GURLs for VisitedLinkMaster.
92class URLIteratorFromURLRows
93    : public visitedlink::VisitedLinkMaster::URLIterator {
94 public:
95  explicit URLIteratorFromURLRows(const history::URLRows& url_rows)
96      : itr_(url_rows.begin()),
97        end_(url_rows.end()) {
98  }
99
100  virtual const GURL& NextURL() OVERRIDE {
101    return (itr_++)->url();
102  }
103
104  virtual bool HasNextURL() const OVERRIDE {
105    return itr_ != end_;
106  }
107
108 private:
109  history::URLRows::const_iterator itr_;
110  history::URLRows::const_iterator end_;
111
112  DISALLOW_COPY_AND_ASSIGN(URLIteratorFromURLRows);
113};
114
115// Callback from WebHistoryService::ExpireWebHistory().
116void ExpireWebHistoryComplete(
117    history::WebHistoryService::Request* request,
118    bool success) {
119  // Ignore the result and delete the request.
120  delete request;
121}
122
123}  // namespace
124
125// Sends messages from the backend to us on the main thread. This must be a
126// separate class from the history service so that it can hold a reference to
127// the history service (otherwise we would have to manually AddRef and
128// Release when the Backend has a reference to us).
129class HistoryService::BackendDelegate : public HistoryBackend::Delegate {
130 public:
131  BackendDelegate(
132      const base::WeakPtr<HistoryService>& history_service,
133      const scoped_refptr<base::SequencedTaskRunner>& service_task_runner,
134      Profile* profile)
135      : history_service_(history_service),
136        service_task_runner_(service_task_runner),
137        profile_(profile) {
138  }
139
140  virtual void NotifyProfileError(int backend_id,
141                                  sql::InitStatus init_status) OVERRIDE {
142    // Send to the history service on the main thread.
143    service_task_runner_->PostTask(
144        FROM_HERE,
145        base::Bind(&HistoryService::NotifyProfileError, history_service_,
146                   backend_id, init_status));
147  }
148
149  virtual void SetInMemoryBackend(int backend_id,
150      history::InMemoryHistoryBackend* backend) OVERRIDE {
151    // Send the backend to the history service on the main thread.
152    scoped_ptr<history::InMemoryHistoryBackend> in_memory_backend(backend);
153    service_task_runner_->PostTask(
154        FROM_HERE,
155        base::Bind(&HistoryService::SetInMemoryBackend, history_service_,
156                   backend_id, base::Passed(&in_memory_backend)));
157  }
158
159  virtual void BroadcastNotifications(
160      int type,
161      history::HistoryDetails* details) OVERRIDE {
162    // Send the notification on the history thread.
163    if (content::NotificationService::current()) {
164      content::Details<history::HistoryDetails> det(details);
165      content::NotificationService::current()->Notify(
166          type, content::Source<Profile>(profile_), det);
167    }
168    // Send the notification to the history service on the main thread.
169    service_task_runner_->PostTask(
170        FROM_HERE,
171        base::Bind(&HistoryService::BroadcastNotificationsHelper,
172                   history_service_, type, base::Owned(details)));
173  }
174
175  virtual void DBLoaded(int backend_id) OVERRIDE {
176    service_task_runner_->PostTask(
177        FROM_HERE,
178        base::Bind(&HistoryService::OnDBLoaded, history_service_,
179                   backend_id));
180  }
181
182  virtual void StartTopSitesMigration(int backend_id) OVERRIDE {
183    service_task_runner_->PostTask(
184        FROM_HERE,
185        base::Bind(&HistoryService::StartTopSitesMigration,
186                   history_service_, backend_id));
187  }
188
189  virtual void NotifyVisitDBObserversOnAddVisit(
190      const history::BriefVisitInfo& info) OVERRIDE {
191    service_task_runner_->PostTask(
192        FROM_HERE,
193        base::Bind(&HistoryService::NotifyVisitDBObserversOnAddVisit,
194                   history_service_, info));
195  }
196
197 private:
198  const base::WeakPtr<HistoryService> history_service_;
199  const scoped_refptr<base::SequencedTaskRunner> service_task_runner_;
200  Profile* const profile_;
201};
202
203// The history thread is intentionally not a BrowserThread because the
204// sync integration unit tests depend on being able to create more than one
205// history thread.
206HistoryService::HistoryService()
207    : weak_ptr_factory_(this),
208      thread_(new base::Thread(kHistoryThreadName)),
209      profile_(NULL),
210      backend_loaded_(false),
211      current_backend_id_(-1),
212      bookmark_service_(NULL),
213      no_db_(false),
214      needs_top_sites_migration_(false) {
215}
216
217HistoryService::HistoryService(Profile* profile)
218    : weak_ptr_factory_(this),
219      thread_(new base::Thread(kHistoryThreadName)),
220      profile_(profile),
221      visitedlink_master_(new visitedlink::VisitedLinkMaster(
222          profile, this, true)),
223      backend_loaded_(false),
224      current_backend_id_(-1),
225      bookmark_service_(NULL),
226      no_db_(false),
227      needs_top_sites_migration_(false) {
228  DCHECK(profile_);
229  registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED,
230                 content::Source<Profile>(profile_));
231  registrar_.Add(this, chrome::NOTIFICATION_TEMPLATE_URL_REMOVED,
232                 content::Source<Profile>(profile_));
233}
234
235HistoryService::~HistoryService() {
236  DCHECK(thread_checker_.CalledOnValidThread());
237  // Shutdown the backend. This does nothing if Cleanup was already invoked.
238  Cleanup();
239}
240
241bool HistoryService::BackendLoaded() {
242  DCHECK(thread_checker_.CalledOnValidThread());
243  // NOTE: We start the backend loading even though it completes asynchronously
244  // and thus won't affect the return value of this function.  This is because
245  // callers of this assume that if the backend isn't yet loaded it will be
246  // soon, so they will either listen for notifications or just retry this call
247  // later.  If we've purged the backend, we haven't necessarily restarted it
248  // loading by now, so we need to trigger the load in order to maintain that
249  // expectation.
250  LoadBackendIfNecessary();
251  return backend_loaded_;
252}
253
254void HistoryService::UnloadBackend() {
255  DCHECK(thread_checker_.CalledOnValidThread());
256  if (!history_backend_.get())
257    return;  // Already unloaded.
258
259  // Get rid of the in-memory backend.
260  in_memory_backend_.reset();
261
262  // Give the InMemoryURLIndex a chance to shutdown.
263  if (in_memory_url_index_)
264    in_memory_url_index_->ShutDown();
265
266  // The backend's destructor must run on the history thread since it is not
267  // threadsafe. So this thread must not be the last thread holding a reference
268  // to the backend, or a crash could happen.
269  //
270  // We have a reference to the history backend. There is also an extra
271  // reference held by our delegate installed in the backend, which
272  // HistoryBackend::Closing will release. This means if we scheduled a call
273  // to HistoryBackend::Closing and *then* released our backend reference, there
274  // will be a race between us and the backend's Closing function to see who is
275  // the last holder of a reference. If the backend thread's Closing manages to
276  // run before we release our backend refptr, the last reference will be held
277  // by this thread and the destructor will be called from here.
278  //
279  // Therefore, we create a closure to run the Closing operation first. This
280  // holds a reference to the backend. Then we release our reference, then we
281  // schedule the task to run. After the task runs, it will delete its reference
282  // from the history thread, ensuring everything works properly.
283  //
284  // TODO(ajwong): Cleanup HistoryBackend lifetime issues.
285  //     See http://crbug.com/99767.
286  history_backend_->AddRef();
287  base::Closure closing_task =
288      base::Bind(&HistoryBackend::Closing, history_backend_.get());
289  ScheduleTask(PRIORITY_NORMAL, closing_task);
290  closing_task.Reset();
291  HistoryBackend* raw_ptr = history_backend_.get();
292  history_backend_ = NULL;
293  thread_->message_loop()->ReleaseSoon(FROM_HERE, raw_ptr);
294}
295
296void HistoryService::Cleanup() {
297  DCHECK(thread_checker_.CalledOnValidThread());
298  if (!thread_) {
299    // We've already cleaned up.
300    return;
301  }
302
303  weak_ptr_factory_.InvalidateWeakPtrs();
304
305  // Unload the backend.
306  UnloadBackend();
307
308  // Delete the thread, which joins with the background thread. We defensively
309  // NULL the pointer before deleting it in case somebody tries to use it
310  // during shutdown, but this shouldn't happen.
311  base::Thread* thread = thread_;
312  thread_ = NULL;
313  delete thread;
314}
315
316void HistoryService::NotifyRenderProcessHostDestruction(const void* host) {
317  DCHECK(thread_checker_.CalledOnValidThread());
318  ScheduleAndForget(PRIORITY_NORMAL,
319                    &HistoryBackend::NotifyRenderProcessHostDestruction, host);
320}
321
322history::URLDatabase* HistoryService::InMemoryDatabase() {
323  DCHECK(thread_checker_.CalledOnValidThread());
324  // NOTE: See comments in BackendLoaded() as to why we call
325  // LoadBackendIfNecessary() here even though it won't affect the return value
326  // for this call.
327  LoadBackendIfNecessary();
328  if (in_memory_backend_)
329    return in_memory_backend_->db();
330  return NULL;
331}
332
333bool HistoryService::GetTypedCountForURL(const GURL& url, int* typed_count) {
334  DCHECK(thread_checker_.CalledOnValidThread());
335  history::URLRow url_row;
336  if (!GetRowForURL(url, &url_row))
337    return false;
338  *typed_count = url_row.typed_count();
339  return true;
340}
341
342bool HistoryService::GetLastVisitTimeForURL(const GURL& url,
343                                            base::Time* last_visit) {
344  DCHECK(thread_checker_.CalledOnValidThread());
345  history::URLRow url_row;
346  if (!GetRowForURL(url, &url_row))
347    return false;
348  *last_visit = url_row.last_visit();
349  return true;
350}
351
352bool HistoryService::GetVisitCountForURL(const GURL& url, int* visit_count) {
353  DCHECK(thread_checker_.CalledOnValidThread());
354  history::URLRow url_row;
355  if (!GetRowForURL(url, &url_row))
356    return false;
357  *visit_count = url_row.visit_count();
358  return true;
359}
360
361history::TypedUrlSyncableService* HistoryService::GetTypedUrlSyncableService()
362    const {
363  return history_backend_->GetTypedUrlSyncableService();
364}
365
366void HistoryService::Shutdown() {
367  DCHECK(thread_checker_.CalledOnValidThread());
368  // It's possible that bookmarks haven't loaded and history is waiting for
369  // bookmarks to complete loading. In such a situation history can't shutdown
370  // (meaning if we invoked history_service_->Cleanup now, we would
371  // deadlock). To break the deadlock we tell BookmarkModel it's about to be
372  // deleted so that it can release the signal history is waiting on, allowing
373  // history to shutdown (history_service_->Cleanup to complete). In such a
374  // scenario history sees an incorrect view of bookmarks, but it's better
375  // than a deadlock.
376  BookmarkModel* bookmark_model = static_cast<BookmarkModel*>(
377      BookmarkModelFactory::GetForProfileIfExists(profile_));
378  if (bookmark_model)
379    bookmark_model->Shutdown();
380
381  Cleanup();
382}
383
384void HistoryService::SetKeywordSearchTermsForURL(const GURL& url,
385                                                 TemplateURLID keyword_id,
386                                                 const string16& term) {
387  DCHECK(thread_checker_.CalledOnValidThread());
388  ScheduleAndForget(PRIORITY_UI,
389                    &HistoryBackend::SetKeywordSearchTermsForURL,
390                    url, keyword_id, term);
391}
392
393void HistoryService::DeleteAllSearchTermsForKeyword(
394    TemplateURLID keyword_id) {
395  DCHECK(thread_checker_.CalledOnValidThread());
396  ScheduleAndForget(PRIORITY_UI,
397                    &HistoryBackend::DeleteAllSearchTermsForKeyword,
398                    keyword_id);
399}
400
401HistoryService::Handle HistoryService::GetMostRecentKeywordSearchTerms(
402    TemplateURLID keyword_id,
403    const string16& prefix,
404    int max_count,
405    CancelableRequestConsumerBase* consumer,
406    const GetMostRecentKeywordSearchTermsCallback& callback) {
407  DCHECK(thread_checker_.CalledOnValidThread());
408  return Schedule(PRIORITY_UI, &HistoryBackend::GetMostRecentKeywordSearchTerms,
409                  consumer,
410                  new history::GetMostRecentKeywordSearchTermsRequest(callback),
411                  keyword_id, prefix, max_count);
412}
413
414void HistoryService::URLsNoLongerBookmarked(const std::set<GURL>& urls) {
415  DCHECK(thread_checker_.CalledOnValidThread());
416  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::URLsNoLongerBookmarked,
417                    urls);
418}
419
420void HistoryService::ScheduleDBTask(history::HistoryDBTask* task,
421                                    CancelableRequestConsumerBase* consumer) {
422  DCHECK(thread_checker_.CalledOnValidThread());
423  history::HistoryDBTaskRequest* request = new history::HistoryDBTaskRequest(
424      base::Bind(&history::HistoryDBTask::DoneRunOnMainThread, task));
425  request->value = task;  // The value is the task to execute.
426  Schedule(PRIORITY_UI, &HistoryBackend::ProcessDBTask, consumer, request);
427}
428
429HistoryService::Handle HistoryService::QuerySegmentUsageSince(
430    CancelableRequestConsumerBase* consumer,
431    const Time from_time,
432    int max_result_count,
433    const SegmentQueryCallback& callback) {
434  DCHECK(thread_checker_.CalledOnValidThread());
435  return Schedule(PRIORITY_UI, &HistoryBackend::QuerySegmentUsage,
436                  consumer, new history::QuerySegmentUsageRequest(callback),
437                  from_time, max_result_count);
438}
439
440void HistoryService::IncreaseSegmentDuration(const GURL& url,
441                                             Time time,
442                                             base::TimeDelta delta) {
443  DCHECK(thread_checker_.CalledOnValidThread());
444  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::IncreaseSegmentDuration,
445                    url, time, delta);
446}
447
448HistoryService::Handle HistoryService::QuerySegmentDurationSince(
449    CancelableRequestConsumerBase* consumer,
450    base::Time from_time,
451    int max_result_count,
452    const SegmentQueryCallback& callback) {
453  DCHECK(thread_checker_.CalledOnValidThread());
454  return Schedule(PRIORITY_UI, &HistoryBackend::QuerySegmentDuration,
455                  consumer, new history::QuerySegmentUsageRequest(callback),
456                  from_time, max_result_count);
457}
458
459void HistoryService::FlushForTest(const base::Closure& flushed) {
460  thread_->message_loop_proxy()->PostTaskAndReply(
461      FROM_HERE, base::Bind(&base::DoNothing), flushed);
462}
463
464void HistoryService::SetOnBackendDestroyTask(const base::Closure& task) {
465  DCHECK(thread_checker_.CalledOnValidThread());
466  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetOnBackendDestroyTask,
467                    base::MessageLoop::current(), task);
468}
469
470void HistoryService::AddPage(const GURL& url,
471                             Time time,
472                             const void* id_scope,
473                             int32 page_id,
474                             const GURL& referrer,
475                             const history::RedirectList& redirects,
476                             content::PageTransition transition,
477                             history::VisitSource visit_source,
478                             bool did_replace_entry) {
479  DCHECK(thread_checker_.CalledOnValidThread());
480  AddPage(
481      history::HistoryAddPageArgs(url, time, id_scope, page_id, referrer,
482                                  redirects, transition, visit_source,
483                                  did_replace_entry));
484}
485
486void HistoryService::AddPage(const GURL& url,
487                             base::Time time,
488                             history::VisitSource visit_source) {
489  DCHECK(thread_checker_.CalledOnValidThread());
490  AddPage(
491      history::HistoryAddPageArgs(url, time, NULL, 0, GURL(),
492                                  history::RedirectList(),
493                                  content::PAGE_TRANSITION_LINK,
494                                  visit_source, false));
495}
496
497void HistoryService::AddPage(const history::HistoryAddPageArgs& add_page_args) {
498  DCHECK(thread_checker_.CalledOnValidThread());
499  DCHECK(thread_) << "History service being called after cleanup";
500
501  // Filter out unwanted URLs. We don't add auto-subframe URLs. They are a
502  // large part of history (think iframes for ads) and we never display them in
503  // history UI. We will still add manual subframes, which are ones the user
504  // has clicked on to get.
505  if (!CanAddURL(add_page_args.url))
506    return;
507
508  // Add link & all redirects to visited link list.
509  if (visitedlink_master_) {
510    visitedlink_master_->AddURL(add_page_args.url);
511
512    if (!add_page_args.redirects.empty()) {
513      // We should not be asked to add a page in the middle of a redirect chain.
514      DCHECK_EQ(add_page_args.url,
515                add_page_args.redirects[add_page_args.redirects.size() - 1]);
516
517      // We need the !redirects.empty() condition above since size_t is unsigned
518      // and will wrap around when we subtract one from a 0 size.
519      for (size_t i = 0; i < add_page_args.redirects.size() - 1; i++)
520        visitedlink_master_->AddURL(add_page_args.redirects[i]);
521    }
522  }
523
524  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::AddPage, add_page_args);
525}
526
527void HistoryService::AddPageNoVisitForBookmark(const GURL& url,
528                                               const string16& title) {
529  DCHECK(thread_checker_.CalledOnValidThread());
530  if (!CanAddURL(url))
531    return;
532
533  ScheduleAndForget(PRIORITY_NORMAL,
534                    &HistoryBackend::AddPageNoVisitForBookmark, url, title);
535}
536
537void HistoryService::SetPageTitle(const GURL& url,
538                                  const string16& title) {
539  DCHECK(thread_checker_.CalledOnValidThread());
540  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetPageTitle, url, title);
541}
542
543void HistoryService::UpdateWithPageEndTime(const void* host,
544                                           int32 page_id,
545                                           const GURL& url,
546                                           Time end_ts) {
547  DCHECK(thread_checker_.CalledOnValidThread());
548  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateWithPageEndTime,
549                    host, page_id, url, end_ts);
550}
551
552void HistoryService::AddPageWithDetails(const GURL& url,
553                                        const string16& title,
554                                        int visit_count,
555                                        int typed_count,
556                                        Time last_visit,
557                                        bool hidden,
558                                        history::VisitSource visit_source) {
559  DCHECK(thread_checker_.CalledOnValidThread());
560  // Filter out unwanted URLs.
561  if (!CanAddURL(url))
562    return;
563
564  // Add to the visited links system.
565  if (visitedlink_master_)
566    visitedlink_master_->AddURL(url);
567
568  history::URLRow row(url);
569  row.set_title(title);
570  row.set_visit_count(visit_count);
571  row.set_typed_count(typed_count);
572  row.set_last_visit(last_visit);
573  row.set_hidden(hidden);
574
575  history::URLRows rows;
576  rows.push_back(row);
577
578  ScheduleAndForget(PRIORITY_NORMAL,
579                    &HistoryBackend::AddPagesWithDetails, rows, visit_source);
580}
581
582void HistoryService::AddPagesWithDetails(const history::URLRows& info,
583                                         history::VisitSource visit_source) {
584  DCHECK(thread_checker_.CalledOnValidThread());
585  // Add to the visited links system.
586  if (visitedlink_master_) {
587    std::vector<GURL> urls;
588    urls.reserve(info.size());
589    for (history::URLRows::const_iterator i = info.begin(); i != info.end();
590         ++i)
591      urls.push_back(i->url());
592
593    visitedlink_master_->AddURLs(urls);
594  }
595
596  ScheduleAndForget(PRIORITY_NORMAL,
597                    &HistoryBackend::AddPagesWithDetails, info, visit_source);
598}
599
600void HistoryService::SetPageContents(const GURL& url,
601                                     const string16& contents) {
602  DCHECK(thread_checker_.CalledOnValidThread());
603  if (!CanAddURL(url))
604    return;
605
606  ScheduleAndForget(PRIORITY_LOW, &HistoryBackend::SetPageContents,
607                    url, contents);
608}
609
610HistoryService::Handle HistoryService::GetPageThumbnail(
611    const GURL& page_url,
612    CancelableRequestConsumerBase* consumer,
613    const ThumbnailDataCallback& callback) {
614  DCHECK(thread_checker_.CalledOnValidThread());
615  return Schedule(PRIORITY_NORMAL, &HistoryBackend::GetPageThumbnail, consumer,
616                  new history::GetPageThumbnailRequest(callback), page_url);
617}
618
619CancelableTaskTracker::TaskId HistoryService::GetFavicons(
620    const std::vector<GURL>& icon_urls,
621    int icon_types,
622    int desired_size_in_dip,
623    const std::vector<ui::ScaleFactor>& desired_scale_factors,
624    const FaviconService::FaviconResultsCallback& callback,
625    CancelableTaskTracker* tracker) {
626  DCHECK(thread_checker_.CalledOnValidThread());
627  LoadBackendIfNecessary();
628
629  std::vector<chrome::FaviconBitmapResult>* results =
630      new std::vector<chrome::FaviconBitmapResult>();
631  return tracker->PostTaskAndReply(
632      thread_->message_loop_proxy().get(),
633      FROM_HERE,
634      base::Bind(&HistoryBackend::GetFavicons,
635                 history_backend_.get(),
636                 icon_urls,
637                 icon_types,
638                 desired_size_in_dip,
639                 desired_scale_factors,
640                 results),
641      base::Bind(&RunWithFaviconResults, callback, base::Owned(results)));
642}
643
644CancelableTaskTracker::TaskId HistoryService::GetFaviconsForURL(
645    const GURL& page_url,
646    int icon_types,
647    int desired_size_in_dip,
648    const std::vector<ui::ScaleFactor>& desired_scale_factors,
649    const FaviconService::FaviconResultsCallback& callback,
650    CancelableTaskTracker* tracker) {
651  DCHECK(thread_checker_.CalledOnValidThread());
652  LoadBackendIfNecessary();
653
654  std::vector<chrome::FaviconBitmapResult>* results =
655      new std::vector<chrome::FaviconBitmapResult>();
656  return tracker->PostTaskAndReply(
657      thread_->message_loop_proxy().get(),
658      FROM_HERE,
659      base::Bind(&HistoryBackend::GetFaviconsForURL,
660                 history_backend_.get(),
661                 page_url,
662                 icon_types,
663                 desired_size_in_dip,
664                 desired_scale_factors,
665                 results),
666      base::Bind(&RunWithFaviconResults, callback, base::Owned(results)));
667}
668
669CancelableTaskTracker::TaskId HistoryService::GetFaviconForID(
670    chrome::FaviconID favicon_id,
671    int desired_size_in_dip,
672    ui::ScaleFactor desired_scale_factor,
673    const FaviconService::FaviconResultsCallback& callback,
674    CancelableTaskTracker* tracker) {
675  DCHECK(thread_checker_.CalledOnValidThread());
676  LoadBackendIfNecessary();
677
678  std::vector<chrome::FaviconBitmapResult>* results =
679      new std::vector<chrome::FaviconBitmapResult>();
680  return tracker->PostTaskAndReply(
681      thread_->message_loop_proxy().get(),
682      FROM_HERE,
683      base::Bind(&HistoryBackend::GetFaviconForID,
684                 history_backend_.get(),
685                 favicon_id,
686                 desired_size_in_dip,
687                 desired_scale_factor,
688                 results),
689      base::Bind(&RunWithFaviconResults, callback, base::Owned(results)));
690}
691
692CancelableTaskTracker::TaskId HistoryService::UpdateFaviconMappingsAndFetch(
693    const GURL& page_url,
694    const std::vector<GURL>& icon_urls,
695    int icon_types,
696    int desired_size_in_dip,
697    const std::vector<ui::ScaleFactor>& desired_scale_factors,
698    const FaviconService::FaviconResultsCallback& callback,
699    CancelableTaskTracker* tracker) {
700  DCHECK(thread_checker_.CalledOnValidThread());
701  LoadBackendIfNecessary();
702
703  std::vector<chrome::FaviconBitmapResult>* results =
704      new std::vector<chrome::FaviconBitmapResult>();
705  return tracker->PostTaskAndReply(
706      thread_->message_loop_proxy().get(),
707      FROM_HERE,
708      base::Bind(&HistoryBackend::UpdateFaviconMappingsAndFetch,
709                 history_backend_.get(),
710                 page_url,
711                 icon_urls,
712                 icon_types,
713                 desired_size_in_dip,
714                 desired_scale_factors,
715                 results),
716      base::Bind(&RunWithFaviconResults, callback, base::Owned(results)));
717}
718
719void HistoryService::MergeFavicon(
720    const GURL& page_url,
721    const GURL& icon_url,
722    chrome::IconType icon_type,
723    scoped_refptr<base::RefCountedMemory> bitmap_data,
724    const gfx::Size& pixel_size) {
725  DCHECK(thread_checker_.CalledOnValidThread());
726  if (!CanAddURL(page_url))
727    return;
728
729  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::MergeFavicon, page_url,
730                    icon_url, icon_type, bitmap_data, pixel_size);
731}
732
733void HistoryService::SetFavicons(
734    const GURL& page_url,
735    chrome::IconType icon_type,
736    const std::vector<chrome::FaviconBitmapData>& favicon_bitmap_data) {
737  DCHECK(thread_checker_.CalledOnValidThread());
738  if (!CanAddURL(page_url))
739    return;
740
741  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetFavicons, page_url,
742      icon_type, favicon_bitmap_data);
743}
744
745void HistoryService::SetFaviconsOutOfDateForPage(const GURL& page_url) {
746  DCHECK(thread_checker_.CalledOnValidThread());
747  ScheduleAndForget(PRIORITY_NORMAL,
748                    &HistoryBackend::SetFaviconsOutOfDateForPage, page_url);
749}
750
751void HistoryService::CloneFavicons(const GURL& old_page_url,
752                                   const GURL& new_page_url) {
753  DCHECK(thread_checker_.CalledOnValidThread());
754  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::CloneFavicons,
755                    old_page_url, new_page_url);
756}
757
758void HistoryService::SetImportedFavicons(
759    const std::vector<ImportedFaviconUsage>& favicon_usage) {
760  DCHECK(thread_checker_.CalledOnValidThread());
761  ScheduleAndForget(PRIORITY_NORMAL,
762                    &HistoryBackend::SetImportedFavicons, favicon_usage);
763}
764
765HistoryService::Handle HistoryService::QueryURL(
766    const GURL& url,
767    bool want_visits,
768    CancelableRequestConsumerBase* consumer,
769    const QueryURLCallback& callback) {
770  DCHECK(thread_checker_.CalledOnValidThread());
771  return Schedule(PRIORITY_UI, &HistoryBackend::QueryURL, consumer,
772                  new history::QueryURLRequest(callback), url, want_visits);
773}
774
775// Downloads -------------------------------------------------------------------
776
777// Handle creation of a download by creating an entry in the history service's
778// 'downloads' table.
779void HistoryService::CreateDownload(
780    const history::DownloadRow& create_info,
781    const HistoryService::DownloadCreateCallback& callback) {
782  DCHECK(thread_) << "History service being called after cleanup";
783  DCHECK(thread_checker_.CalledOnValidThread());
784  LoadBackendIfNecessary();
785  bool* success = new bool(false);
786  thread_->message_loop_proxy()->PostTaskAndReply(
787      FROM_HERE,
788      base::Bind(&HistoryBackend::CreateDownload,
789                 history_backend_.get(),
790                 create_info,
791                 success),
792      base::Bind(&DerefPODType<bool>, callback, base::Owned(success)));
793}
794
795void HistoryService::GetNextDownloadId(
796    const content::DownloadIdCallback& callback) {
797  DCHECK(thread_) << "History service being called after cleanup";
798  DCHECK(thread_checker_.CalledOnValidThread());
799  LoadBackendIfNecessary();
800  uint32* next_id = new uint32(content::DownloadItem::kInvalidId);
801  thread_->message_loop_proxy()->PostTaskAndReply(
802      FROM_HERE,
803      base::Bind(&HistoryBackend::GetNextDownloadId,
804                 history_backend_.get(),
805                 next_id),
806      base::Bind(&DerefPODType<uint32>, callback, base::Owned(next_id)));
807}
808
809// Handle queries for a list of all downloads in the history database's
810// 'downloads' table.
811void HistoryService::QueryDownloads(
812    const DownloadQueryCallback& callback) {
813  DCHECK(thread_) << "History service being called after cleanup";
814  DCHECK(thread_checker_.CalledOnValidThread());
815  LoadBackendIfNecessary();
816  std::vector<history::DownloadRow>* rows =
817    new std::vector<history::DownloadRow>();
818  scoped_ptr<std::vector<history::DownloadRow> > scoped_rows(rows);
819  // Beware! The first Bind() does not simply |scoped_rows.get()| because
820  // base::Passed(&scoped_rows) nullifies |scoped_rows|, and compilers do not
821  // guarantee that the first Bind's arguments are evaluated before the second
822  // Bind's arguments.
823  thread_->message_loop_proxy()->PostTaskAndReply(
824      FROM_HERE,
825      base::Bind(&HistoryBackend::QueryDownloads, history_backend_.get(), rows),
826      base::Bind(callback, base::Passed(&scoped_rows)));
827}
828
829// Handle updates for a particular download. This is a 'fire and forget'
830// operation, so we don't need to be called back.
831void HistoryService::UpdateDownload(const history::DownloadRow& data) {
832  DCHECK(thread_checker_.CalledOnValidThread());
833  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateDownload, data);
834}
835
836void HistoryService::RemoveDownloads(const std::set<uint32>& ids) {
837  DCHECK(thread_checker_.CalledOnValidThread());
838  ScheduleAndForget(PRIORITY_NORMAL,
839                    &HistoryBackend::RemoveDownloads, ids);
840}
841
842HistoryService::Handle HistoryService::QueryHistory(
843    const string16& text_query,
844    const history::QueryOptions& options,
845    CancelableRequestConsumerBase* consumer,
846    const QueryHistoryCallback& callback) {
847  DCHECK(thread_checker_.CalledOnValidThread());
848  return Schedule(PRIORITY_UI, &HistoryBackend::QueryHistory, consumer,
849                  new history::QueryHistoryRequest(callback),
850                  text_query, options);
851}
852
853HistoryService::Handle HistoryService::QueryRedirectsFrom(
854    const GURL& from_url,
855    CancelableRequestConsumerBase* consumer,
856    const QueryRedirectsCallback& callback) {
857  DCHECK(thread_checker_.CalledOnValidThread());
858  return Schedule(PRIORITY_UI, &HistoryBackend::QueryRedirectsFrom, consumer,
859      new history::QueryRedirectsRequest(callback), from_url);
860}
861
862HistoryService::Handle HistoryService::QueryRedirectsTo(
863    const GURL& to_url,
864    CancelableRequestConsumerBase* consumer,
865    const QueryRedirectsCallback& callback) {
866  DCHECK(thread_checker_.CalledOnValidThread());
867  return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryRedirectsTo, consumer,
868      new history::QueryRedirectsRequest(callback), to_url);
869}
870
871HistoryService::Handle HistoryService::GetVisibleVisitCountToHost(
872    const GURL& url,
873    CancelableRequestConsumerBase* consumer,
874    const GetVisibleVisitCountToHostCallback& callback) {
875  DCHECK(thread_checker_.CalledOnValidThread());
876  return Schedule(PRIORITY_UI, &HistoryBackend::GetVisibleVisitCountToHost,
877      consumer, new history::GetVisibleVisitCountToHostRequest(callback), url);
878}
879
880HistoryService::Handle HistoryService::QueryTopURLsAndRedirects(
881    int result_count,
882    CancelableRequestConsumerBase* consumer,
883    const QueryTopURLsAndRedirectsCallback& callback) {
884  DCHECK(thread_checker_.CalledOnValidThread());
885  return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryTopURLsAndRedirects,
886      consumer, new history::QueryTopURLsAndRedirectsRequest(callback),
887      result_count);
888}
889
890HistoryService::Handle HistoryService::QueryMostVisitedURLs(
891    int result_count,
892    int days_back,
893    CancelableRequestConsumerBase* consumer,
894    const QueryMostVisitedURLsCallback& callback) {
895  DCHECK(thread_checker_.CalledOnValidThread());
896  return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryMostVisitedURLs,
897                  consumer,
898                  new history::QueryMostVisitedURLsRequest(callback),
899                  result_count, days_back);
900}
901
902HistoryService::Handle HistoryService::QueryFilteredURLs(
903    int result_count,
904    const history::VisitFilter& filter,
905    bool extended_info,
906    CancelableRequestConsumerBase* consumer,
907    const QueryFilteredURLsCallback& callback) {
908  DCHECK(thread_checker_.CalledOnValidThread());
909  return Schedule(PRIORITY_NORMAL,
910                  &HistoryBackend::QueryFilteredURLs,
911                  consumer,
912                  new history::QueryFilteredURLsRequest(callback),
913                  result_count, filter, extended_info);
914}
915
916void HistoryService::Observe(int type,
917                             const content::NotificationSource& source,
918                             const content::NotificationDetails& details) {
919  DCHECK(thread_checker_.CalledOnValidThread());
920  if (!thread_)
921    return;
922
923  switch (type) {
924    case chrome::NOTIFICATION_HISTORY_URLS_DELETED: {
925      // Update the visited link system for deleted URLs. We will update the
926      // visited link system for added URLs as soon as we get the add
927      // notification (we don't have to wait for the backend, which allows us to
928      // be faster to update the state).
929      //
930      // For deleted URLs, we don't typically know what will be deleted since
931      // delete notifications are by time. We would also like to be more
932      // respectful of privacy and never tell the user something is gone when it
933      // isn't. Therefore, we update the delete URLs after the fact.
934      if (visitedlink_master_) {
935        content::Details<history::URLsDeletedDetails> deleted_details(details);
936
937        if (deleted_details->all_history) {
938          visitedlink_master_->DeleteAllURLs();
939        } else {
940          URLIteratorFromURLRows iterator(deleted_details->rows);
941          visitedlink_master_->DeleteURLs(&iterator);
942        }
943      }
944      break;
945    }
946
947    case chrome::NOTIFICATION_TEMPLATE_URL_REMOVED:
948      DeleteAllSearchTermsForKeyword(
949          *(content::Details<TemplateURLID>(details).ptr()));
950      break;
951
952    default:
953      NOTREACHED();
954  }
955}
956
957void HistoryService::RebuildTable(
958    const scoped_refptr<URLEnumerator>& enumerator) {
959  DCHECK(thread_checker_.CalledOnValidThread());
960  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::IterateURLs, enumerator);
961}
962
963bool HistoryService::Init(const base::FilePath& history_dir,
964                          BookmarkService* bookmark_service,
965                          bool no_db) {
966  DCHECK(thread_checker_.CalledOnValidThread());
967  if (!thread_->Start()) {
968    Cleanup();
969    return false;
970  }
971
972  history_dir_ = history_dir;
973  bookmark_service_ = bookmark_service;
974  no_db_ = no_db;
975
976  if (profile_) {
977    std::string languages =
978        profile_->GetPrefs()->GetString(prefs::kAcceptLanguages);
979    in_memory_url_index_.reset(
980        new history::InMemoryURLIndex(profile_, history_dir_, languages));
981    in_memory_url_index_->Init();
982  }
983
984  // Create the history backend.
985  LoadBackendIfNecessary();
986
987  if (visitedlink_master_) {
988    bool result = visitedlink_master_->Init();
989    DCHECK(result);
990  }
991
992  return true;
993}
994
995void HistoryService::ScheduleAutocomplete(HistoryURLProvider* provider,
996                                          HistoryURLProviderParams* params) {
997  DCHECK(thread_checker_.CalledOnValidThread());
998  ScheduleAndForget(PRIORITY_UI, &HistoryBackend::ScheduleAutocomplete,
999                    scoped_refptr<HistoryURLProvider>(provider), params);
1000}
1001
1002void HistoryService::ScheduleTask(SchedulePriority priority,
1003                                  const base::Closure& task) {
1004  DCHECK(thread_checker_.CalledOnValidThread());
1005  CHECK(thread_);
1006  CHECK(thread_->message_loop());
1007  // TODO(brettw): Do prioritization.
1008  thread_->message_loop()->PostTask(FROM_HERE, task);
1009}
1010
1011// static
1012bool HistoryService::CanAddURL(const GURL& url) {
1013  if (!url.is_valid())
1014    return false;
1015
1016  // TODO: We should allow kChromeUIScheme URLs if they have been explicitly
1017  // typed.  Right now, however, these are marked as typed even when triggered
1018  // by a shortcut or menu action.
1019  if (url.SchemeIs(chrome::kJavaScriptScheme) ||
1020      url.SchemeIs(chrome::kChromeDevToolsScheme) ||
1021      url.SchemeIs(chrome::kChromeUIScheme) ||
1022      url.SchemeIs(content::kViewSourceScheme) ||
1023      url.SchemeIs(chrome::kChromeInternalScheme))
1024    return false;
1025
1026  // Allow all about: and chrome: URLs except about:blank, since the user may
1027  // like to see "chrome://memory/", etc. in their history and autocomplete.
1028  if (url == GURL(content::kAboutBlankURL))
1029    return false;
1030
1031  return true;
1032}
1033
1034base::WeakPtr<HistoryService> HistoryService::AsWeakPtr() {
1035  DCHECK(thread_checker_.CalledOnValidThread());
1036  return weak_ptr_factory_.GetWeakPtr();
1037}
1038
1039syncer::SyncMergeResult HistoryService::MergeDataAndStartSyncing(
1040    syncer::ModelType type,
1041    const syncer::SyncDataList& initial_sync_data,
1042    scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
1043    scoped_ptr<syncer::SyncErrorFactory> error_handler) {
1044  DCHECK(thread_checker_.CalledOnValidThread());
1045  DCHECK_EQ(type, syncer::HISTORY_DELETE_DIRECTIVES);
1046  delete_directive_handler_.Start(this, initial_sync_data,
1047                                  sync_processor.Pass());
1048  return syncer::SyncMergeResult(type);
1049}
1050
1051void HistoryService::StopSyncing(syncer::ModelType type) {
1052  DCHECK(thread_checker_.CalledOnValidThread());
1053  DCHECK_EQ(type, syncer::HISTORY_DELETE_DIRECTIVES);
1054  delete_directive_handler_.Stop();
1055}
1056
1057syncer::SyncDataList HistoryService::GetAllSyncData(
1058    syncer::ModelType type) const {
1059  DCHECK(thread_checker_.CalledOnValidThread());
1060  DCHECK_EQ(type, syncer::HISTORY_DELETE_DIRECTIVES);
1061  // TODO(akalin): Keep track of existing delete directives.
1062  return syncer::SyncDataList();
1063}
1064
1065syncer::SyncError HistoryService::ProcessSyncChanges(
1066    const tracked_objects::Location& from_here,
1067    const syncer::SyncChangeList& change_list) {
1068  delete_directive_handler_.ProcessSyncChanges(this, change_list);
1069  return syncer::SyncError();
1070}
1071
1072syncer::SyncError HistoryService::ProcessLocalDeleteDirective(
1073    const sync_pb::HistoryDeleteDirectiveSpecifics& delete_directive) {
1074  DCHECK(thread_checker_.CalledOnValidThread());
1075  return delete_directive_handler_.ProcessLocalDeleteDirective(
1076      delete_directive);
1077}
1078
1079void HistoryService::SetInMemoryBackend(
1080    int backend_id, scoped_ptr<history::InMemoryHistoryBackend> mem_backend) {
1081  DCHECK(thread_checker_.CalledOnValidThread());
1082  if (!history_backend_.get() || current_backend_id_ != backend_id) {
1083    DVLOG(1) << "Message from obsolete backend";
1084    // mem_backend is deleted.
1085    return;
1086  }
1087  DCHECK(!in_memory_backend_) << "Setting mem DB twice";
1088  in_memory_backend_.reset(mem_backend.release());
1089
1090  // The database requires additional initialization once we own it.
1091  in_memory_backend_->AttachToHistoryService(profile_);
1092}
1093
1094void HistoryService::NotifyProfileError(int backend_id,
1095                                        sql::InitStatus init_status) {
1096  DCHECK(thread_checker_.CalledOnValidThread());
1097  if (!history_backend_.get() || current_backend_id_ != backend_id) {
1098    DVLOG(1) << "Message from obsolete backend";
1099    return;
1100  }
1101  ShowProfileErrorDialog(
1102      (init_status == sql::INIT_FAILURE) ?
1103      IDS_COULDNT_OPEN_PROFILE_ERROR : IDS_PROFILE_TOO_NEW_ERROR);
1104}
1105
1106void HistoryService::DeleteURL(const GURL& url) {
1107  DCHECK(thread_checker_.CalledOnValidThread());
1108  // We will update the visited links when we observe the delete notifications.
1109  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::DeleteURL, url);
1110}
1111
1112void HistoryService::DeleteURLsForTest(const std::vector<GURL>& urls) {
1113  DCHECK(thread_checker_.CalledOnValidThread());
1114  // We will update the visited links when we observe the delete
1115  // notifications.
1116  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::DeleteURLs, urls);
1117}
1118
1119void HistoryService::ExpireHistoryBetween(
1120    const std::set<GURL>& restrict_urls,
1121    Time begin_time,
1122    Time end_time,
1123    const base::Closure& callback,
1124    CancelableTaskTracker* tracker) {
1125  DCHECK(thread_);
1126  DCHECK(thread_checker_.CalledOnValidThread());
1127  DCHECK(history_backend_.get());
1128  tracker->PostTaskAndReply(thread_->message_loop_proxy().get(),
1129                            FROM_HERE,
1130                            base::Bind(&HistoryBackend::ExpireHistoryBetween,
1131                                       history_backend_,
1132                                       restrict_urls,
1133                                       begin_time,
1134                                       end_time),
1135                            callback);
1136}
1137
1138void HistoryService::ExpireHistory(
1139    const std::vector<history::ExpireHistoryArgs>& expire_list,
1140    const base::Closure& callback,
1141    CancelableTaskTracker* tracker) {
1142  DCHECK(thread_);
1143  DCHECK(thread_checker_.CalledOnValidThread());
1144  DCHECK(history_backend_.get());
1145  tracker->PostTaskAndReply(
1146      thread_->message_loop_proxy().get(),
1147      FROM_HERE,
1148      base::Bind(&HistoryBackend::ExpireHistory, history_backend_, expire_list),
1149      callback);
1150}
1151
1152void HistoryService::ExpireLocalAndRemoteHistoryBetween(
1153    const std::set<GURL>& restrict_urls,
1154    Time begin_time,
1155    Time end_time,
1156    const base::Closure& callback,
1157    CancelableTaskTracker* tracker) {
1158  // TODO(dubroy): This should be factored out into a separate class that
1159  // dispatches deletions to the proper places.
1160
1161  history::WebHistoryService* web_history =
1162      WebHistoryServiceFactory::GetForProfile(profile_);
1163  if (web_history) {
1164    // TODO(dubroy): This API does not yet support deletion of specific URLs.
1165    DCHECK(restrict_urls.empty());
1166
1167    delete_directive_handler_.CreateDeleteDirectives(
1168        std::set<int64>(), begin_time, end_time);
1169
1170    // Attempt online deletion from the history server, but ignore the result.
1171    // Deletion directives ensure that the results will eventually be deleted.
1172    // Pass ownership of the request to the callback.
1173    scoped_ptr<history::WebHistoryService::Request> request =
1174        web_history->ExpireHistoryBetween(
1175            restrict_urls, begin_time, end_time,
1176            base::Bind(&ExpireWebHistoryComplete));
1177
1178    // The request will be freed when the callback is called.
1179    CHECK(request.release());
1180  }
1181  ExpireHistoryBetween(restrict_urls, begin_time, end_time, callback, tracker);
1182}
1183
1184void HistoryService::BroadcastNotificationsHelper(
1185    int type,
1186    history::HistoryDetails* details) {
1187  DCHECK(thread_checker_.CalledOnValidThread());
1188  // TODO(evanm): this is currently necessitated by generate_profile, which
1189  // runs without a browser process. generate_profile should really create
1190  // a browser process, at which point this check can then be nuked.
1191  if (!g_browser_process)
1192    return;
1193
1194  if (!thread_)
1195    return;
1196
1197  // The source of all of our notifications is the profile. Note that this
1198  // pointer is NULL in unit tests.
1199  content::Source<Profile> source(profile_);
1200
1201  // The details object just contains the pointer to the object that the
1202  // backend has allocated for us. The receiver of the notification will cast
1203  // this to the proper type.
1204  content::Details<history::HistoryDetails> det(details);
1205
1206  content::NotificationService::current()->Notify(type, source, det);
1207}
1208
1209void HistoryService::LoadBackendIfNecessary() {
1210  DCHECK(thread_checker_.CalledOnValidThread());
1211  if (!thread_ || history_backend_.get())
1212    return;  // Failed to init, or already started loading.
1213
1214  ++current_backend_id_;
1215  scoped_refptr<HistoryBackend> backend(
1216      new HistoryBackend(history_dir_,
1217                         current_backend_id_,
1218                         new BackendDelegate(
1219                             weak_ptr_factory_.GetWeakPtr(),
1220                             base::ThreadTaskRunnerHandle::Get(),
1221                             profile_),
1222                         bookmark_service_));
1223  history_backend_.swap(backend);
1224
1225  // There may not be a profile when unit testing.
1226  std::string languages;
1227  if (profile_) {
1228    PrefService* prefs = profile_->GetPrefs();
1229    languages = prefs->GetString(prefs::kAcceptLanguages);
1230  }
1231  ScheduleAndForget(PRIORITY_UI, &HistoryBackend::Init, languages, no_db_);
1232}
1233
1234void HistoryService::OnDBLoaded(int backend_id) {
1235  DCHECK(thread_checker_.CalledOnValidThread());
1236  if (!history_backend_.get() || current_backend_id_ != backend_id) {
1237    DVLOG(1) << "Message from obsolete backend";
1238    return;
1239  }
1240  backend_loaded_ = true;
1241  content::NotificationService::current()->Notify(
1242      chrome::NOTIFICATION_HISTORY_LOADED,
1243      content::Source<Profile>(profile_),
1244      content::Details<HistoryService>(this));
1245  if (thread_ && profile_) {
1246    // We don't want to force creation of TopSites.
1247    history::TopSites* ts = profile_->GetTopSitesWithoutCreating();
1248    if (ts)
1249      ts->HistoryLoaded();
1250  }
1251}
1252
1253bool HistoryService::GetRowForURL(const GURL& url, history::URLRow* url_row) {
1254  DCHECK(thread_checker_.CalledOnValidThread());
1255  history::URLDatabase* db = InMemoryDatabase();
1256  return db && (db->GetRowForURL(url, url_row) != 0);
1257}
1258
1259void HistoryService::StartTopSitesMigration(int backend_id) {
1260  DCHECK(thread_checker_.CalledOnValidThread());
1261  if (!history_backend_.get() || current_backend_id_ != backend_id) {
1262    DVLOG(1) << "Message from obsolete backend";
1263    return;
1264  }
1265  needs_top_sites_migration_ = true;
1266  if (thread_ && profile_) {
1267    // We don't want to force creation of TopSites.
1268    history::TopSites* ts = profile_->GetTopSitesWithoutCreating();
1269    if (ts)
1270      ts->MigrateFromHistory();
1271  }
1272}
1273
1274void HistoryService::OnTopSitesReady() {
1275  DCHECK(thread_checker_.CalledOnValidThread());
1276  ScheduleAndForget(PRIORITY_NORMAL,
1277                    &HistoryBackend::MigrateThumbnailsDatabase);
1278}
1279
1280void HistoryService::AddVisitDatabaseObserver(
1281    history::VisitDatabaseObserver* observer) {
1282  DCHECK(thread_checker_.CalledOnValidThread());
1283  visit_database_observers_.AddObserver(observer);
1284}
1285
1286void HistoryService::RemoveVisitDatabaseObserver(
1287    history::VisitDatabaseObserver* observer) {
1288  DCHECK(thread_checker_.CalledOnValidThread());
1289  visit_database_observers_.RemoveObserver(observer);
1290}
1291
1292void HistoryService::NotifyVisitDBObserversOnAddVisit(
1293    const history::BriefVisitInfo& info) {
1294  DCHECK(thread_checker_.CalledOnValidThread());
1295  FOR_EACH_OBSERVER(history::VisitDatabaseObserver, visit_database_observers_,
1296                    OnAddVisit(info));
1297}
1298