1 // Copyright 2014 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 "components/suggestions/blacklist_store.h" 6 7 #include <set> 8 #include <string> 9 10 #include "base/base64.h" 11 #include "base/metrics/histogram.h" 12 #include "base/prefs/pref_service.h" 13 #include "components/pref_registry/pref_registry_syncable.h" 14 #include "components/suggestions/suggestions_pref_names.h" 15 16 namespace suggestions { 17 18 namespace { 19 20 void PopulateBlacklistSet(const SuggestionsBlacklist& blacklist_proto, 21 std::set<std::string>* blacklist_set) { 22 blacklist_set->clear(); 23 for (int i = 0; i < blacklist_proto.urls_size(); ++i) { 24 blacklist_set->insert(blacklist_proto.urls(i)); 25 } 26 } 27 28 void PopulateBlacklistProto(const std::set<std::string>& blacklist_set, 29 SuggestionsBlacklist* blacklist_proto) { 30 blacklist_proto->Clear(); 31 for (std::set<std::string>::const_iterator it = blacklist_set.begin(); 32 it != blacklist_set.end(); ++it) { 33 blacklist_proto->add_urls(*it); 34 } 35 } 36 37 } // namespace 38 39 BlacklistStore::BlacklistStore(PrefService* profile_prefs) 40 : pref_service_(profile_prefs) { 41 DCHECK(pref_service_); 42 43 // Log the blacklist's size. A single BlacklistStore is created for the 44 // SuggestionsService; this will run once. 45 SuggestionsBlacklist blacklist_proto; 46 LoadBlacklist(&blacklist_proto); 47 UMA_HISTOGRAM_COUNTS_10000("Suggestions.LocalBlacklistSize", 48 blacklist_proto.urls_size()); 49 } 50 51 BlacklistStore::~BlacklistStore() {} 52 53 bool BlacklistStore::BlacklistUrl(const GURL& url) { 54 if (!url.is_valid()) return false; 55 56 SuggestionsBlacklist blacklist_proto; 57 LoadBlacklist(&blacklist_proto); 58 59 std::set<std::string> blacklist_set; 60 PopulateBlacklistSet(blacklist_proto, &blacklist_set); 61 62 if (!blacklist_set.insert(url.spec()).second) { 63 // |url| was already in the blacklist. 64 return true; 65 } 66 67 PopulateBlacklistProto(blacklist_set, &blacklist_proto); 68 return StoreBlacklist(blacklist_proto); 69 } 70 71 bool BlacklistStore::GetFirstUrlFromBlacklist(GURL* url) { 72 SuggestionsBlacklist blacklist; 73 LoadBlacklist(&blacklist); 74 if (!blacklist.urls_size()) return false; 75 GURL blacklisted(blacklist.urls(0)); 76 url->Swap(&blacklisted); 77 return true; 78 } 79 80 bool BlacklistStore::RemoveUrl(const GURL& url) { 81 if (!url.is_valid()) return false; 82 const std::string removal_candidate = url.spec(); 83 84 SuggestionsBlacklist blacklist; 85 LoadBlacklist(&blacklist); 86 87 SuggestionsBlacklist updated_blacklist; 88 for (int i = 0; i < blacklist.urls_size(); ++i) { 89 if (blacklist.urls(i) != removal_candidate) 90 updated_blacklist.add_urls(blacklist.urls(i)); 91 } 92 93 return StoreBlacklist(updated_blacklist); 94 } 95 96 void BlacklistStore::FilterSuggestions(SuggestionsProfile* profile) { 97 if (!profile->suggestions_size()) 98 return; // Empty profile, nothing to filter. 99 100 SuggestionsBlacklist blacklist_proto; 101 if (!LoadBlacklist(&blacklist_proto)) { 102 // There was an error loading the blacklist. The blacklist was cleared and 103 // there's nothing to be done about it. 104 return; 105 } 106 if (!blacklist_proto.urls_size()) 107 return; // Empty blacklist, nothing to filter. 108 109 std::set<std::string> blacklist_set; 110 PopulateBlacklistSet(blacklist_proto, &blacklist_set); 111 112 // Populate the filtered suggestions. 113 SuggestionsProfile filtered_profile; 114 for (int i = 0; i < profile->suggestions_size(); ++i) { 115 if (blacklist_set.find(profile->suggestions(i).url()) == 116 blacklist_set.end()) { 117 // This suggestion is not blacklisted. 118 ChromeSuggestion* suggestion = filtered_profile.add_suggestions(); 119 // Note: swapping! 120 suggestion->Swap(profile->mutable_suggestions(i)); 121 } 122 } 123 124 // Swap |profile| and |filtered_profile|. 125 profile->Swap(&filtered_profile); 126 } 127 128 // static 129 void BlacklistStore::RegisterProfilePrefs( 130 user_prefs::PrefRegistrySyncable* registry) { 131 registry->RegisterStringPref( 132 prefs::kSuggestionsBlacklist, std::string(), 133 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 134 } 135 136 bool BlacklistStore::LoadBlacklist(SuggestionsBlacklist* blacklist) { 137 DCHECK(blacklist); 138 139 const std::string base64_blacklist_data = 140 pref_service_->GetString(prefs::kSuggestionsBlacklist); 141 if (base64_blacklist_data.empty()) { 142 blacklist->Clear(); 143 return false; 144 } 145 146 // If the decode process fails, assume the pref value is corrupt and clear it. 147 std::string blacklist_data; 148 if (!base::Base64Decode(base64_blacklist_data, &blacklist_data) || 149 !blacklist->ParseFromString(blacklist_data)) { 150 VLOG(1) << "Suggestions blacklist data in profile pref is corrupt, " 151 << " clearing it."; 152 blacklist->Clear(); 153 ClearBlacklist(); 154 return false; 155 } 156 157 return true; 158 } 159 160 bool BlacklistStore::StoreBlacklist(const SuggestionsBlacklist& blacklist) { 161 std::string blacklist_data; 162 if (!blacklist.SerializeToString(&blacklist_data)) return false; 163 164 std::string base64_blacklist_data; 165 base::Base64Encode(blacklist_data, &base64_blacklist_data); 166 167 pref_service_->SetString(prefs::kSuggestionsBlacklist, base64_blacklist_data); 168 return true; 169 } 170 171 void BlacklistStore::ClearBlacklist() { 172 pref_service_->ClearPref(prefs::kSuggestionsBlacklist); 173 } 174 175 } // namespace suggestions 176