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