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