1dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/history/in_memory_history_backend.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include <set>
821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include <vector>
921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/command_line.h"
113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/time.h"
123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/utf_string_conversions.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/browser_process.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/history/history_notifications.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/history/in_memory_database.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/history/in_memory_url_index.h"
173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/history/url_database.h"
1821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile.h"
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_switches.h"
20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_details.h"
21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_source.h"
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace history {
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// If a page becomes starred we use this id in place of the real starred id.
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// See note in OnURLsStarred.
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const StarID kBogusStarredID = 0x0FFFFFFF;
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochInMemoryHistoryBackend::InMemoryHistoryBackend()
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : profile_(NULL) {
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochInMemoryHistoryBackend::~InMemoryHistoryBackend() {
34dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (index_.get())
35dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    index_->ShutDown();
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickbool InMemoryHistoryBackend::Init(const FilePath& history_filename,
39dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                                  const FilePath& history_dir,
40731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                                  URLDatabase* db,
41731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                                  const std::string& languages) {
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  db_.reset(new InMemoryDatabase);
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool success = db_->InitFromDisk(history_filename);
44dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (CommandLine::ForCurrentProcess()->HasSwitch(
45dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen          switches::kEnableHistoryQuickProvider) &&
46dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      !CommandLine::ForCurrentProcess()->HasSwitch(
47dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen          switches::kDisableHistoryQuickProvider)) {
48dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    index_.reset(new InMemoryURLIndex(history_dir));
49dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
50731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    index_->Init(db, languages);
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return success;
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid InMemoryHistoryBackend::AttachToHistoryService(Profile* profile) {
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!db_.get()) {
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED();
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  profile_ = profile;
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(evanm): this is currently necessitated by generate_profile, which
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // runs without a browser process. generate_profile should really create
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // a browser process, at which point this check can then be nuked.
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!g_browser_process)
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Register for the notifications we care about.
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We only want notifications for the associated profile.
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Source<Profile> source(profile_);
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  registrar_.Add(this, NotificationType::HISTORY_URL_VISITED, source);
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  registrar_.Add(this, NotificationType::HISTORY_TYPED_URLS_MODIFIED, source);
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED, source);
75513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  registrar_.Add(this, NotificationType::HISTORY_KEYWORD_SEARCH_TERM_UPDATED,
76513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                 source);
77513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  registrar_.Add(this, NotificationType::TEMPLATE_URL_REMOVED, source);
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid InMemoryHistoryBackend::Observe(NotificationType type,
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                     const NotificationSource& source,
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                     const NotificationDetails& details) {
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  switch (type.value) {
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case NotificationType::HISTORY_URL_VISITED: {
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      Details<history::URLVisitedDetails> visited_details(details);
86513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      PageTransition::Type primary_type =
87513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch          PageTransition::StripQualifier(visited_details->transition);
88513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      if (visited_details->row.typed_count() > 0 ||
893f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen          primary_type == PageTransition::KEYWORD ||
903f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen          HasKeyword(visited_details->row.url())) {
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        URLsModifiedDetails modified_details;
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        modified_details.changed_urls.push_back(visited_details->row);
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        OnTypedURLsModified(modified_details);
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
97513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    case NotificationType::HISTORY_KEYWORD_SEARCH_TERM_UPDATED:
98513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      OnKeywordSearchTermUpdated(
99513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch          *Details<history::KeywordSearchTermDetails>(details).ptr());
100513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      break;
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case NotificationType::HISTORY_TYPED_URLS_MODIFIED:
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      OnTypedURLsModified(
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          *Details<history::URLsModifiedDetails>(details).ptr());
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case NotificationType::HISTORY_URLS_DELETED:
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      OnURLsDeleted(*Details<history::URLsDeletedDetails>(details).ptr());
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
108513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    case NotificationType::TEMPLATE_URL_REMOVED:
109513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      db_->DeleteAllSearchTermsForKeyword(
110513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch          *(Details<TemplateURLID>(details).ptr()));
111513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      break;
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    default:
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // For simplicity, the unit tests send us all notifications, even when
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // we haven't registered for them, so don't assert here.
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid InMemoryHistoryBackend::OnTypedURLsModified(
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const URLsModifiedDetails& details) {
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(db_.get());
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Add or update the URLs.
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(brettw) currently the rows in the in-memory database don't match the
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // IDs in the main database. This sucks. Instead of Add and Remove, we should
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // have Sync(), which would take the ID if it's given and add it.
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<history::URLRow>::const_iterator i;
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (i = details.changed_urls.begin();
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       i != details.changed_urls.end(); i++) {
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    URLID id = db_->GetRowForURL(i->url(), NULL);
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (id)
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      db_->UpdateURLRow(id, *i);
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    else
135dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      id = db_->AddURL(*i);
136dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    if (index_.get())
137dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      index_->UpdateURL(id, *i);
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid InMemoryHistoryBackend::OnURLsDeleted(const URLsDeletedDetails& details) {
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(db_.get());
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (details.all_history) {
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // When all history is deleted, the individual URLs won't be listed. Just
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // create a new database to quickly clear everything out.
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    db_.reset(new InMemoryDatabase);
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!db_->InitFromScratch())
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      db_.reset();
150dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    if (index_.get())
151dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      index_->ReloadFromHistory(db_.get(), true);
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Delete all matching URLs in our database.
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (std::set<GURL>::const_iterator i = details.urls.begin();
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       i != details.urls.end(); ++i) {
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    URLID id = db_->GetRowForURL(*i, NULL);
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (id) {
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // We typically won't have most of them since we only have a subset of
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // history, so ignore errors.
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      db_->DeleteURLRow(id);
163dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      if (index_.get())
164dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        index_->DeleteURL(id);
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
169513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid InMemoryHistoryBackend::OnKeywordSearchTermUpdated(
170513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    const KeywordSearchTermDetails& details) {
171513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // The url won't exist for new search terms (as the user hasn't typed it), so
172513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // we force it to be added. If we end up adding a URL it won't be
173513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // autocompleted as the typed count is 0.
174513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  URLRow url_row;
175513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  URLID url_id;
176513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (!db_->GetRowForURL(details.url, &url_row)) {
177513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    // Because this row won't have a typed count the title and other stuff
178513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    // doesn't matter. If the user ends up typing the url we'll update the title
179513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    // in OnTypedURLsModified.
180513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    URLRow new_row(details.url);
181513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    new_row.set_last_visit(base::Time::Now());
182513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    url_id = db_->AddURL(new_row);
183513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    if (!url_id)
184513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      return;  // Error adding.
185513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  } else {
186513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    url_id = url_row.id();
187513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  }
188513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
189513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  db_->SetKeywordSearchTermsForURL(url_id, details.keyword_id, details.term);
190513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}
191513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
1923f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenbool InMemoryHistoryBackend::HasKeyword(const GURL& url) {
1933f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  URLID id = db_->GetRowForURL(url, NULL);
1943f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (!id)
1953f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
1963f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
1973f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  return db_->GetKeywordSearchTermRow(id, NULL);
1983f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen}
1993f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace history
201