1// Copyright (c) 2011 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/memory/ref_counted.h"
29#include "base/message_loop.h"
30#include "base/path_service.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_process.h"
35#include "chrome/browser/history/download_create_info.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_database.h"
40#include "chrome/browser/history/in_memory_history_backend.h"
41#include "chrome/browser/history/top_sites.h"
42#include "chrome/browser/prefs/pref_service.h"
43#include "chrome/browser/profiles/profile.h"
44#include "chrome/browser/ui/profile_error_dialog.h"
45#include "chrome/browser/visitedlink/visitedlink_master.h"
46#include "chrome/common/chrome_constants.h"
47#include "chrome/common/pref_names.h"
48#include "chrome/common/thumbnail_score.h"
49#include "chrome/common/url_constants.h"
50#include "content/browser/browser_thread.h"
51#include "content/common/notification_service.h"
52#include "grit/chromium_strings.h"
53#include "grit/generated_resources.h"
54#include "third_party/skia/include/core/SkBitmap.h"
55
56using base::Time;
57using history::HistoryBackend;
58
59namespace {
60
61static const char* kHistoryThreadName = "Chrome_HistoryThread";
62
63}  // namespace
64
65// Sends messages from the backend to us on the main thread. This must be a
66// separate class from the history service so that it can hold a reference to
67// the history service (otherwise we would have to manually AddRef and
68// Release when the Backend has a reference to us).
69class HistoryService::BackendDelegate : public HistoryBackend::Delegate {
70 public:
71  explicit BackendDelegate(HistoryService* history_service)
72      : history_service_(history_service),
73        message_loop_(MessageLoop::current()) {
74  }
75
76  virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {
77    // Send to the history service on the main thread.
78    message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
79        &HistoryService::NotifyProfileError, init_status));
80  }
81
82  virtual void SetInMemoryBackend(
83      history::InMemoryHistoryBackend* backend) OVERRIDE {
84    // Send the backend to the history service on the main thread.
85    message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
86        &HistoryService::SetInMemoryBackend, backend));
87  }
88
89  virtual void BroadcastNotifications(
90      NotificationType type,
91      history::HistoryDetails* details) OVERRIDE {
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() OVERRIDE {
105    message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
106        &HistoryService::OnDBLoaded));
107  }
108
109  virtual void StartTopSitesMigration() OVERRIDE {
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      needs_top_sites_migration_(false) {
132}
133
134HistoryService::HistoryService(Profile* profile)
135    : thread_(new base::Thread(kHistoryThreadName)),
136      profile_(profile),
137      backend_loaded_(false),
138      bookmark_service_(NULL),
139      no_db_(false),
140      needs_top_sites_migration_(false) {
141  DCHECK(profile_);
142  registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED,
143                 Source<Profile>(profile_));
144  registrar_.Add(this, NotificationType::TEMPLATE_URL_REMOVED,
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::AddPageNoVisitForBookmark(const GURL& url) {
361  if (!CanAddURL(url))
362    return;
363
364  ScheduleAndForget(PRIORITY_NORMAL,
365                    &HistoryBackend::AddPageNoVisitForBookmark, url);
366}
367
368void HistoryService::SetPageTitle(const GURL& url,
369                                  const string16& title) {
370  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetPageTitle, url, title);
371}
372
373void HistoryService::AddPageWithDetails(const GURL& url,
374                                        const string16& title,
375                                        int visit_count,
376                                        int typed_count,
377                                        Time last_visit,
378                                        bool hidden,
379                                        history::VisitSource visit_source) {
380  // Filter out unwanted URLs.
381  if (!CanAddURL(url))
382    return;
383
384  // Add to the visited links system.
385  VisitedLinkMaster* visited_links;
386  if (profile_ && (visited_links = profile_->GetVisitedLinkMaster()))
387    visited_links->AddURL(url);
388
389  history::URLRow row(url);
390  row.set_title(title);
391  row.set_visit_count(visit_count);
392  row.set_typed_count(typed_count);
393  row.set_last_visit(last_visit);
394  row.set_hidden(hidden);
395
396  std::vector<history::URLRow> rows;
397  rows.push_back(row);
398
399  ScheduleAndForget(PRIORITY_NORMAL,
400                    &HistoryBackend::AddPagesWithDetails, rows, visit_source);
401}
402
403void HistoryService::AddPagesWithDetails(
404    const std::vector<history::URLRow>& info,
405    history::VisitSource visit_source) {
406
407  // Add to the visited links system.
408  VisitedLinkMaster* visited_links;
409  if (profile_ && (visited_links = profile_->GetVisitedLinkMaster())) {
410    std::vector<GURL> urls;
411    urls.reserve(info.size());
412    for (std::vector<history::URLRow>::const_iterator i = info.begin();
413         i != info.end();
414         ++i)
415      urls.push_back(i->url());
416
417    visited_links->AddURLs(urls);
418  }
419
420  ScheduleAndForget(PRIORITY_NORMAL,
421                    &HistoryBackend::AddPagesWithDetails, info, visit_source);
422}
423
424void HistoryService::SetPageContents(const GURL& url,
425                                     const string16& contents) {
426  if (!CanAddURL(url))
427    return;
428
429  ScheduleAndForget(PRIORITY_LOW, &HistoryBackend::SetPageContents,
430                    url, contents);
431}
432
433void HistoryService::SetPageThumbnail(const GURL& page_url,
434                                      const SkBitmap& thumbnail,
435                                      const ThumbnailScore& score) {
436  if (!CanAddURL(page_url))
437    return;
438
439  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetPageThumbnail,
440                    page_url, thumbnail, score);
441}
442
443HistoryService::Handle HistoryService::GetPageThumbnail(
444    const GURL& page_url,
445    CancelableRequestConsumerBase* consumer,
446    ThumbnailDataCallback* callback) {
447  return Schedule(PRIORITY_NORMAL, &HistoryBackend::GetPageThumbnail, consumer,
448                  new history::GetPageThumbnailRequest(callback), page_url);
449}
450
451void HistoryService::GetFavicon(FaviconService::GetFaviconRequest* request,
452                                const GURL& icon_url,
453                                history::IconType icon_type) {
454  Schedule(PRIORITY_NORMAL, &HistoryBackend::GetFavicon, NULL, request,
455           icon_url, icon_type);
456}
457
458void HistoryService::UpdateFaviconMappingAndFetch(
459    FaviconService::GetFaviconRequest* request,
460    const GURL& page_url,
461    const GURL& icon_url,
462    history::IconType icon_type) {
463  Schedule(PRIORITY_NORMAL, &HistoryBackend::UpdateFaviconMappingAndFetch, NULL,
464           request, page_url, icon_url, history::FAVICON);
465}
466
467void HistoryService::GetFaviconForURL(
468    FaviconService::GetFaviconRequest* request,
469    const GURL& page_url,
470    int icon_types) {
471  Schedule(PRIORITY_NORMAL, &HistoryBackend::GetFaviconForURL, NULL, request,
472           page_url, icon_types);
473}
474
475void HistoryService::SetFavicon(const GURL& page_url,
476                                const GURL& icon_url,
477                                const std::vector<unsigned char>& image_data,
478                                history::IconType icon_type) {
479  if (!CanAddURL(page_url))
480    return;
481
482  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetFavicon,
483      page_url, icon_url,
484      scoped_refptr<RefCountedMemory>(new RefCountedBytes(image_data)),
485      icon_type);
486}
487
488void HistoryService::SetFaviconOutOfDateForPage(const GURL& page_url) {
489  ScheduleAndForget(PRIORITY_NORMAL,
490                    &HistoryBackend::SetFaviconOutOfDateForPage, page_url);
491}
492
493void HistoryService::SetImportedFavicons(
494    const std::vector<history::ImportedFaviconUsage>& favicon_usage) {
495  ScheduleAndForget(PRIORITY_NORMAL,
496                    &HistoryBackend::SetImportedFavicons, favicon_usage);
497}
498
499void HistoryService::IterateURLs(URLEnumerator* enumerator) {
500  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::IterateURLs, enumerator);
501}
502
503HistoryService::Handle HistoryService::QueryURL(
504    const GURL& url,
505    bool want_visits,
506    CancelableRequestConsumerBase* consumer,
507    QueryURLCallback* callback) {
508  return Schedule(PRIORITY_UI, &HistoryBackend::QueryURL, consumer,
509                  new history::QueryURLRequest(callback), url, want_visits);
510}
511
512// Downloads -------------------------------------------------------------------
513
514// Handle creation of a download by creating an entry in the history service's
515// 'downloads' table.
516HistoryService::Handle HistoryService::CreateDownload(
517    const DownloadCreateInfo& create_info,
518    CancelableRequestConsumerBase* consumer,
519    HistoryService::DownloadCreateCallback* callback) {
520  return Schedule(PRIORITY_NORMAL, &HistoryBackend::CreateDownload, consumer,
521                  new history::DownloadCreateRequest(callback), create_info);
522}
523
524// Handle queries for a list of all downloads in the history database's
525// 'downloads' table.
526HistoryService::Handle HistoryService::QueryDownloads(
527    CancelableRequestConsumerBase* consumer,
528    DownloadQueryCallback* callback) {
529  return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryDownloads, consumer,
530                  new history::DownloadQueryRequest(callback));
531}
532
533// Changes all IN_PROGRESS in the database entries to CANCELED.
534// IN_PROGRESS entries are the corrupted entries, not updated by next function
535// because of the crash or some other extremal exit.
536void HistoryService::CleanUpInProgressEntries() {
537  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::CleanUpInProgressEntries);
538}
539
540// Handle updates for a particular download. This is a 'fire and forget'
541// operation, so we don't need to be called back.
542void HistoryService::UpdateDownload(int64 received_bytes,
543                                    int32 state,
544                                    int64 db_handle) {
545  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateDownload,
546                    received_bytes, state, db_handle);
547}
548
549void HistoryService::UpdateDownloadPath(const FilePath& path,
550                                        int64 db_handle) {
551  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateDownloadPath,
552                    path, db_handle);
553}
554
555void HistoryService::RemoveDownload(int64 db_handle) {
556  ScheduleAndForget(PRIORITY_NORMAL,
557                    &HistoryBackend::RemoveDownload, db_handle);
558}
559
560void HistoryService::RemoveDownloadsBetween(Time remove_begin,
561                                            Time remove_end) {
562  ScheduleAndForget(PRIORITY_NORMAL,
563                    &HistoryBackend::RemoveDownloadsBetween,
564                    remove_begin,
565                    remove_end);
566}
567
568HistoryService::Handle HistoryService::QueryHistory(
569    const string16& text_query,
570    const history::QueryOptions& options,
571    CancelableRequestConsumerBase* consumer,
572    QueryHistoryCallback* callback) {
573  return Schedule(PRIORITY_UI, &HistoryBackend::QueryHistory, consumer,
574                  new history::QueryHistoryRequest(callback),
575                  text_query, options);
576}
577
578HistoryService::Handle HistoryService::QueryRedirectsFrom(
579    const GURL& from_url,
580    CancelableRequestConsumerBase* consumer,
581    QueryRedirectsCallback* callback) {
582  return Schedule(PRIORITY_UI, &HistoryBackend::QueryRedirectsFrom, consumer,
583      new history::QueryRedirectsRequest(callback), from_url);
584}
585
586HistoryService::Handle HistoryService::QueryRedirectsTo(
587    const GURL& to_url,
588    CancelableRequestConsumerBase* consumer,
589    QueryRedirectsCallback* callback) {
590  return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryRedirectsTo, consumer,
591      new history::QueryRedirectsRequest(callback), to_url);
592}
593
594HistoryService::Handle HistoryService::GetVisitCountToHost(
595    const GURL& url,
596    CancelableRequestConsumerBase* consumer,
597    GetVisitCountToHostCallback* callback) {
598  return Schedule(PRIORITY_UI, &HistoryBackend::GetVisitCountToHost, consumer,
599      new history::GetVisitCountToHostRequest(callback), url);
600}
601
602HistoryService::Handle HistoryService::QueryTopURLsAndRedirects(
603    int result_count,
604    CancelableRequestConsumerBase* consumer,
605    QueryTopURLsAndRedirectsCallback* callback) {
606  return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryTopURLsAndRedirects,
607      consumer, new history::QueryTopURLsAndRedirectsRequest(callback),
608      result_count);
609}
610
611HistoryService::Handle HistoryService::QueryMostVisitedURLs(
612    int result_count,
613    int days_back,
614    CancelableRequestConsumerBase* consumer,
615    QueryMostVisitedURLsCallback* callback) {
616  return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryMostVisitedURLs,
617                  consumer,
618                  new history::QueryMostVisitedURLsRequest(callback),
619                  result_count, days_back);
620}
621
622void HistoryService::Observe(NotificationType type,
623                             const NotificationSource& source,
624                             const NotificationDetails& details) {
625  if (!thread_)
626    return;
627
628  switch (type.value) {
629    case NotificationType::HISTORY_URLS_DELETED: {
630      // Update the visited link system for deleted URLs. We will update the
631      // visited link system for added URLs as soon as we get the add
632      // notification (we don't have to wait for the backend, which allows us to
633      // be faster to update the state).
634      //
635      // For deleted URLs, we don't typically know what will be deleted since
636      // delete notifications are by time. We would also like to be more
637      // respectful of privacy and never tell the user something is gone when it
638      // isn't. Therefore, we update the delete URLs after the fact.
639      if (!profile_)
640        return;  // No profile, probably unit testing.
641      Details<history::URLsDeletedDetails> deleted_details(details);
642      VisitedLinkMaster* visited_links = profile_->GetVisitedLinkMaster();
643      if (!visited_links)
644        return;  // Nobody to update.
645      if (deleted_details->all_history)
646        visited_links->DeleteAllURLs();
647      else  // Delete individual ones.
648        visited_links->DeleteURLs(deleted_details->urls);
649      break;
650    }
651
652    case NotificationType::TEMPLATE_URL_REMOVED:
653      DeleteAllSearchTermsForKeyword(*(Details<TemplateURLID>(details).ptr()));
654      break;
655
656    default:
657      NOTREACHED();
658  }
659}
660
661bool HistoryService::Init(const FilePath& history_dir,
662                          BookmarkService* bookmark_service,
663                          bool no_db) {
664  if (!thread_->Start()) {
665    Cleanup();
666    return false;
667  }
668
669  history_dir_ = history_dir;
670  bookmark_service_ = bookmark_service;
671  no_db_ = no_db;
672
673  // Create the history backend.
674  LoadBackendIfNecessary();
675  return true;
676}
677
678void HistoryService::ScheduleAutocomplete(HistoryURLProvider* provider,
679                                          HistoryURLProviderParams* params) {
680  ScheduleAndForget(PRIORITY_UI, &HistoryBackend::ScheduleAutocomplete,
681                    scoped_refptr<HistoryURLProvider>(provider), params);
682}
683
684void HistoryService::ScheduleTask(SchedulePriority priority,
685                                  Task* task) {
686  // TODO(brettw): do prioritization.
687  thread_->message_loop()->PostTask(FROM_HERE, task);
688}
689
690// static
691bool HistoryService::CanAddURL(const GURL& url) {
692  if (!url.is_valid())
693    return false;
694
695  // TODO: We should allow kChromeUIScheme URLs if they have been explicitly
696  // typed.  Right now, however, these are marked as typed even when triggered
697  // by a shortcut or menu action.
698  if (url.SchemeIs(chrome::kJavaScriptScheme) ||
699      url.SchemeIs(chrome::kChromeDevToolsScheme) ||
700      url.SchemeIs(chrome::kChromeUIScheme) ||
701      url.SchemeIs(chrome::kViewSourceScheme) ||
702      url.SchemeIs(chrome::kChromeInternalScheme))
703    return false;
704
705  if (url.SchemeIs(chrome::kAboutScheme)) {
706    if (LowerCaseEqualsASCII(url.path(), "blank"))
707      return false;
708    // We allow all other about URLs since the user may like to see things
709    // like "about:memory" or "about:histograms" in their history and
710    // autocomplete.
711  }
712
713  return true;
714}
715
716void HistoryService::SetInMemoryBackend(
717    history::InMemoryHistoryBackend* mem_backend) {
718  DCHECK(!in_memory_backend_.get()) << "Setting mem DB twice";
719  in_memory_backend_.reset(mem_backend);
720
721  // The database requires additional initialization once we own it.
722  in_memory_backend_->AttachToHistoryService(profile_);
723}
724
725void HistoryService::NotifyProfileError(sql::InitStatus init_status) {
726  ShowProfileErrorDialog(
727      (init_status == sql::INIT_FAILURE) ?
728      IDS_COULDNT_OPEN_PROFILE_ERROR : IDS_PROFILE_TOO_NEW_ERROR);
729}
730
731void HistoryService::DeleteURL(const GURL& url) {
732  // We will update the visited links when we observe the delete notifications.
733  ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::DeleteURL, url);
734}
735
736void HistoryService::ExpireHistoryBetween(
737    const std::set<GURL>& restrict_urls,
738    Time begin_time, Time end_time,
739    CancelableRequestConsumerBase* consumer,
740    ExpireHistoryCallback* callback) {
741
742  // We will update the visited links when we observe the delete notifications.
743  Schedule(PRIORITY_UI, &HistoryBackend::ExpireHistoryBetween, consumer,
744           new history::ExpireHistoryRequest(callback),
745           restrict_urls, begin_time, end_time);
746}
747
748void HistoryService::BroadcastNotifications(
749    NotificationType type,
750    history::HistoryDetails* details_deleted) {
751  // We take ownership of the passed-in pointer and delete it. It was made for
752  // us on another thread, so the caller doesn't know when we will handle it.
753  scoped_ptr<history::HistoryDetails> details(details_deleted);
754  // TODO(evanm): this is currently necessitated by generate_profile, which
755  // runs without a browser process. generate_profile should really create
756  // a browser process, at which point this check can then be nuked.
757  if (!g_browser_process)
758    return;
759
760  if (!thread_)
761    return;
762
763  // The source of all of our notifications is the profile. Note that this
764  // pointer is NULL in unit tests.
765  Source<Profile> source(profile_);
766
767  // The details object just contains the pointer to the object that the
768  // backend has allocated for us. The receiver of the notification will cast
769  // this to the proper type.
770  Details<history::HistoryDetails> det(details_deleted);
771
772  NotificationService::current()->Notify(type, source, det);
773}
774
775void HistoryService::LoadBackendIfNecessary() {
776  if (!thread_ || history_backend_)
777    return;  // Failed to init, or already started loading.
778
779  scoped_refptr<HistoryBackend> backend(
780      new HistoryBackend(history_dir_,
781                         new BackendDelegate(this),
782                         bookmark_service_));
783  history_backend_.swap(backend);
784
785  // There may not be a profile when unit testing.
786  std::string languages;
787  if (profile_) {
788    PrefService* prefs = profile_->GetPrefs();
789    languages = prefs->GetString(prefs::kAcceptLanguages);
790  }
791  ScheduleAndForget(PRIORITY_UI, &HistoryBackend::Init, languages, no_db_);
792}
793
794void HistoryService::OnDBLoaded() {
795  backend_loaded_ = true;
796  NotificationService::current()->Notify(NotificationType::HISTORY_LOADED,
797                                         Source<Profile>(profile_),
798                                         Details<HistoryService>(this));
799  if (thread_ && profile_) {
800    // We don't want to force creation of TopSites.
801    history::TopSites* ts = profile_->GetTopSitesWithoutCreating();
802    if (ts)
803      ts->HistoryLoaded();
804  }
805}
806
807void HistoryService::StartTopSitesMigration() {
808  needs_top_sites_migration_ = true;
809  if (thread_ && profile_) {
810    // We don't want to force creation of TopSites.
811    history::TopSites* ts = profile_->GetTopSitesWithoutCreating();
812    if (ts)
813      ts->MigrateFromHistory();
814  }
815}
816
817void HistoryService::OnTopSitesReady() {
818  ScheduleAndForget(PRIORITY_NORMAL,
819                    &HistoryBackend::MigrateThumbnailsDatabase);
820}
821