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/profiles/profile.h" 17 #include "components/history/core/browser/in_memory_database.h" 18 #include "components/history/core/browser/url_database.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 db_.reset(new InMemoryDatabase); 32 return db_->InitFromDisk(history_filename); 33 } 34 35 void InMemoryHistoryBackend::AttachToHistoryService(Profile* profile) { 36 if (!db_) { 37 NOTREACHED(); 38 return; 39 } 40 41 profile_ = profile; 42 43 // TODO(evanm): this is currently necessitated by generate_profile, which 44 // runs without a browser process. generate_profile should really create 45 // a browser process, at which point this check can then be nuked. 46 if (!g_browser_process) 47 return; 48 49 // Register for the notifications we care about. 50 // We only want notifications for the associated profile. 51 content::Source<Profile> source(profile_); 52 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URL_VISITED, source); 53 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, source); 54 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, source); 55 registrar_.Add( 56 this, chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED, source); 57 registrar_.Add( 58 this, chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_DELETED, source); 59 } 60 61 void InMemoryHistoryBackend::DeleteAllSearchTermsForKeyword( 62 KeywordID keyword_id) { 63 // For simplicity, this will not remove the corresponding URLRows, but 64 // this is okay, as the main database does not do so either. 65 db_->DeleteAllSearchTermsForKeyword(keyword_id); 66 } 67 68 void InMemoryHistoryBackend::Observe( 69 int type, 70 const content::NotificationSource& source, 71 const content::NotificationDetails& details) { 72 switch (type) { 73 case chrome::NOTIFICATION_HISTORY_URL_VISITED: 74 OnURLVisitedOrModified(content::Details<URLVisitedDetails>(details)->row); 75 break; 76 case chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED: 77 OnKeywordSearchTermUpdated( 78 *content::Details<KeywordSearchUpdatedDetails>(details).ptr()); 79 break; 80 case chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_DELETED: 81 OnKeywordSearchTermDeleted( 82 *content::Details<KeywordSearchDeletedDetails>(details).ptr()); 83 break; 84 case chrome::NOTIFICATION_HISTORY_URLS_MODIFIED: { 85 const URLsModifiedDetails* modified_details = 86 content::Details<URLsModifiedDetails>(details).ptr(); 87 URLRows::const_iterator it; 88 for (it = modified_details->changed_urls.begin(); 89 it != modified_details->changed_urls.end(); ++it) { 90 OnURLVisitedOrModified(*it); 91 } 92 break; 93 } 94 case chrome::NOTIFICATION_HISTORY_URLS_DELETED: 95 OnURLsDeleted(*content::Details<URLsDeletedDetails>(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::OnURLVisitedOrModified(const URLRow& url_row) { 105 DCHECK(db_); 106 DCHECK(url_row.id()); 107 if (url_row.typed_count() || db_->GetKeywordSearchTermRow(url_row.id(), NULL)) 108 db_->InsertOrUpdateURLRowByID(url_row); 109 else 110 db_->DeleteURLRow(url_row.id()); 111 } 112 113 void InMemoryHistoryBackend::OnURLsDeleted(const URLsDeletedDetails& details) { 114 DCHECK(db_); 115 116 if (details.all_history) { 117 // When all history is deleted, the individual URLs won't be listed. Just 118 // create a new database to quickly clear everything out. 119 db_.reset(new InMemoryDatabase); 120 if (!db_->InitFromScratch()) 121 db_.reset(); 122 return; 123 } 124 125 // Delete all matching URLs in our database. 126 for (URLRows::const_iterator row = details.rows.begin(); 127 row != details.rows.end(); ++row) { 128 // This will also delete the corresponding keyword search term. 129 // Ignore errors, as we typically only cache a subset of URLRows. 130 db_->DeleteURLRow(row->id()); 131 } 132 } 133 134 void InMemoryHistoryBackend::OnKeywordSearchTermUpdated( 135 const KeywordSearchUpdatedDetails& details) { 136 DCHECK(details.url_row.id()); 137 db_->InsertOrUpdateURLRowByID(details.url_row); 138 db_->SetKeywordSearchTermsForURL( 139 details.url_row.id(), details.keyword_id, details.term); 140 } 141 142 void InMemoryHistoryBackend::OnKeywordSearchTermDeleted( 143 const KeywordSearchDeletedDetails& details) { 144 // For simplicity, this will not remove the corresponding URLRow, but this is 145 // okay, as the main database does not do so either. 146 db_->DeleteKeywordSearchTermForURL(details.url_row_id); 147 } 148 149 } // namespace history 150