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, source); 55 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, source); 56 registrar_.Add( 57 this, chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED, source); 58 registrar_.Add( 59 this, chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_DELETED, 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 OnURLVisitedOrModified(content::Details<URLVisitedDetails>(details)->row); 70 break; 71 case chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED: 72 OnKeywordSearchTermUpdated( 73 *content::Details<KeywordSearchUpdatedDetails>(details).ptr()); 74 break; 75 case chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_DELETED: 76 OnKeywordSearchTermDeleted( 77 *content::Details<KeywordSearchDeletedDetails>(details).ptr()); 78 break; 79 case chrome::NOTIFICATION_HISTORY_URLS_MODIFIED: { 80 const URLsModifiedDetails* modified_details = 81 content::Details<URLsModifiedDetails>(details).ptr(); 82 URLRows::const_iterator it; 83 for (it = modified_details->changed_urls.begin(); 84 it != modified_details->changed_urls.end(); ++it) { 85 OnURLVisitedOrModified(*it); 86 } 87 break; 88 } 89 case chrome::NOTIFICATION_HISTORY_URLS_DELETED: 90 OnURLsDeleted(*content::Details<URLsDeletedDetails>(details).ptr()); 91 break; 92 case chrome::NOTIFICATION_TEMPLATE_URL_REMOVED: 93 // For simplicity, this will not remove the corresponding URLRows, but 94 // this is okay, as the main database does not do so either. 95 db_->DeleteAllSearchTermsForKeyword( 96 *(content::Details<TemplateURLID>(details).ptr())); 97 break; 98 default: 99 // For simplicity, the unit tests send us all notifications, even when 100 // we haven't registered for them, so don't assert here. 101 break; 102 } 103 } 104 105 void InMemoryHistoryBackend::OnURLVisitedOrModified(const URLRow& url_row) { 106 DCHECK(db_); 107 DCHECK(url_row.id()); 108 if (url_row.typed_count() || db_->GetKeywordSearchTermRow(url_row.id(), NULL)) 109 db_->InsertOrUpdateURLRowByID(url_row); 110 else 111 db_->DeleteURLRow(url_row.id()); 112 } 113 114 void InMemoryHistoryBackend::OnURLsDeleted(const URLsDeletedDetails& details) { 115 DCHECK(db_); 116 117 if (details.all_history) { 118 // When all history is deleted, the individual URLs won't be listed. Just 119 // create a new database to quickly clear everything out. 120 db_.reset(new InMemoryDatabase); 121 if (!db_->InitFromScratch()) 122 db_.reset(); 123 return; 124 } 125 126 // Delete all matching URLs in our database. 127 for (URLRows::const_iterator row = details.rows.begin(); 128 row != details.rows.end(); ++row) { 129 // This will also delete the corresponding keyword search term. 130 // Ignore errors, as we typically only cache a subset of URLRows. 131 db_->DeleteURLRow(row->id()); 132 } 133 } 134 135 void InMemoryHistoryBackend::OnKeywordSearchTermUpdated( 136 const KeywordSearchUpdatedDetails& details) { 137 DCHECK(details.url_row.id()); 138 db_->InsertOrUpdateURLRowByID(details.url_row); 139 db_->SetKeywordSearchTermsForURL( 140 details.url_row.id(), details.keyword_id, details.term); 141 } 142 143 void InMemoryHistoryBackend::OnKeywordSearchTermDeleted( 144 const KeywordSearchDeletedDetails& details) { 145 // For simplicity, this will not remove the corresponding URLRow, but this is 146 // okay, as the main database does not do so either. 147 db_->DeleteKeywordSearchTermForURL(details.url_row_id); 148 } 149 150 } // namespace history 151