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 #include "chrome/browser/history/in_memory_history_backend.h" 6 7 #include <set> 8 #include <vector> 9 10 #include "base/command_line.h" 11 #include "base/strings/utf_string_conversions.h" 12 #include "base/time/time.h" 13 #include "chrome/browser/browser_process.h" 14 #include "chrome/browser/chrome_notification_types.h" 15 #include "chrome/browser/history/history_notifications.h" 16 #include "chrome/browser/history/in_memory_database.h" 17 #include "chrome/browser/history/url_database.h" 18 #include "chrome/browser/profiles/profile.h" 19 #include "content/public/browser/notification_details.h" 20 #include "content/public/browser/notification_source.h" 21 22 namespace history { 23 24 InMemoryHistoryBackend::InMemoryHistoryBackend() 25 : profile_(NULL) { 26 } 27 28 InMemoryHistoryBackend::~InMemoryHistoryBackend() {} 29 30 bool InMemoryHistoryBackend::Init(const base::FilePath& history_filename, 31 URLDatabase* db) { 32 db_.reset(new InMemoryDatabase); 33 return db_->InitFromDisk(history_filename); 34 } 35 36 void InMemoryHistoryBackend::AttachToHistoryService(Profile* profile) { 37 if (!db_) { 38 NOTREACHED(); 39 return; 40 } 41 42 profile_ = profile; 43 44 // TODO(evanm): this is currently necessitated by generate_profile, which 45 // runs without a browser process. generate_profile should really create 46 // a browser process, at which point this check can then be nuked. 47 if (!g_browser_process) 48 return; 49 50 // Register for the notifications we care about. 51 // We only want notifications for the associated profile. 52 content::Source<Profile> source(profile_); 53 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URL_VISITED, source); 54 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, 55 source); 56 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, source); 57 registrar_.Add(this, 58 chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED, 59 source); 60 registrar_.Add(this, chrome::NOTIFICATION_TEMPLATE_URL_REMOVED, source); 61 } 62 63 void InMemoryHistoryBackend::Observe( 64 int type, 65 const content::NotificationSource& source, 66 const content::NotificationDetails& details) { 67 switch (type) { 68 case chrome::NOTIFICATION_HISTORY_URL_VISITED: { 69 content::Details<history::URLVisitedDetails> visited_details(details); 70 content::PageTransition primary_type = 71 content::PageTransitionStripQualifier(visited_details->transition); 72 if (visited_details->row.typed_count() > 0 || 73 primary_type == content::PAGE_TRANSITION_KEYWORD || 74 HasKeyword(visited_details->row.url())) { 75 URLsModifiedDetails modified_details; 76 modified_details.changed_urls.push_back(visited_details->row); 77 OnTypedURLsModified(modified_details); 78 } 79 break; 80 } 81 case chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED: 82 OnKeywordSearchTermUpdated( 83 *content::Details<history::KeywordSearchTermDetails>(details).ptr()); 84 break; 85 case chrome::NOTIFICATION_HISTORY_URLS_MODIFIED: 86 OnTypedURLsModified( 87 *content::Details<history::URLsModifiedDetails>(details).ptr()); 88 break; 89 case chrome::NOTIFICATION_HISTORY_URLS_DELETED: 90 OnURLsDeleted( 91 *content::Details<history::URLsDeletedDetails>(details).ptr()); 92 break; 93 case chrome::NOTIFICATION_TEMPLATE_URL_REMOVED: 94 db_->DeleteAllSearchTermsForKeyword( 95 *(content::Details<TemplateURLID>(details).ptr())); 96 break; 97 default: 98 // For simplicity, the unit tests send us all notifications, even when 99 // we haven't registered for them, so don't assert here. 100 break; 101 } 102 } 103 104 void InMemoryHistoryBackend::OnTypedURLsModified( 105 const URLsModifiedDetails& details) { 106 DCHECK(db_); 107 108 // Add or update the URLs. 109 // 110 // TODO(brettw) currently the rows in the in-memory database don't match the 111 // IDs in the main database. This sucks. Instead of Add and Remove, we should 112 // have Sync(), which would take the ID if it's given and add it. 113 URLRows::const_iterator i; 114 for (i = details.changed_urls.begin(); 115 i != details.changed_urls.end(); ++i) { 116 if (i->typed_count() > 0) { 117 URLID id = db_->GetRowForURL(i->url(), NULL); 118 if (id) 119 db_->UpdateURLRow(id, *i); 120 else 121 db_->AddURL(*i); 122 } 123 } 124 } 125 126 void InMemoryHistoryBackend::OnURLsDeleted(const URLsDeletedDetails& details) { 127 DCHECK(db_); 128 129 if (details.all_history) { 130 // When all history is deleted, the individual URLs won't be listed. Just 131 // create a new database to quickly clear everything out. 132 db_.reset(new InMemoryDatabase); 133 if (!db_->InitFromScratch()) 134 db_.reset(); 135 return; 136 } 137 138 // Delete all matching URLs in our database. 139 for (URLRows::const_iterator row = details.rows.begin(); 140 row != details.rows.end(); ++row) { 141 // We typically won't have most of them since we only have a subset of 142 // history, so ignore errors. 143 db_->DeleteURLRow(row->id()); 144 } 145 } 146 147 void InMemoryHistoryBackend::OnKeywordSearchTermUpdated( 148 const KeywordSearchTermDetails& details) { 149 // The url won't exist for new search terms (as the user hasn't typed it), so 150 // we force it to be added. If we end up adding a URL it won't be 151 // autocompleted as the typed count is 0. 152 URLRow url_row; 153 URLID url_id; 154 if (!db_->GetRowForURL(details.url, &url_row)) { 155 // Because this row won't have a typed count the title and other stuff 156 // doesn't matter. If the user ends up typing the url we'll update the title 157 // in OnTypedURLsModified. 158 URLRow new_row(details.url); 159 new_row.set_last_visit(base::Time::Now()); 160 url_id = db_->AddURL(new_row); 161 if (!url_id) 162 return; // Error adding. 163 } else { 164 url_id = url_row.id(); 165 } 166 167 db_->SetKeywordSearchTermsForURL(url_id, details.keyword_id, details.term); 168 } 169 170 bool InMemoryHistoryBackend::HasKeyword(const GURL& url) { 171 URLID id = db_->GetRowForURL(url, NULL); 172 if (!id) 173 return false; 174 175 return db_->GetKeywordSearchTermRow(id, NULL); 176 } 177 178 } // namespace history 179