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