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