Roll src/third_party/WebKit e2f1087:c936ac9 (svn 200537:200538)
[chromium-blink-merge.git] / components / suggestions / blacklist_store.cc
blobcc529adb95781c19d25d9ccf70065b1f1a5738b8
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.
5 #include "components/suggestions/blacklist_store.h"
7 #include <algorithm>
8 #include <set>
9 #include <string>
11 #include "base/base64.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/prefs/pref_service.h"
14 #include "components/pref_registry/pref_registry_syncable.h"
15 #include "components/suggestions/suggestions_pref_names.h"
17 using base::TimeDelta;
18 using base::TimeTicks;
20 namespace suggestions {
21 namespace {
23 void PopulateBlacklistSet(const SuggestionsBlacklist& blacklist_proto,
24 std::set<std::string>* blacklist_set) {
25 blacklist_set->clear();
26 for (int i = 0; i < blacklist_proto.urls_size(); ++i) {
27 blacklist_set->insert(blacklist_proto.urls(i));
31 void PopulateBlacklistProto(const std::set<std::string>& blacklist_set,
32 SuggestionsBlacklist* blacklist_proto) {
33 blacklist_proto->Clear();
34 for (std::set<std::string>::const_iterator it = blacklist_set.begin();
35 it != blacklist_set.end(); ++it) {
36 blacklist_proto->add_urls(*it);
40 } // namespace
42 BlacklistStore::BlacklistStore(PrefService* profile_prefs,
43 const base::TimeDelta& upload_delay)
44 : pref_service_(profile_prefs), upload_delay_(upload_delay) {
45 DCHECK(pref_service_);
47 // Log the blacklist's size. A single BlacklistStore is created for the
48 // SuggestionsService; this will run once.
49 SuggestionsBlacklist blacklist_proto;
50 LoadBlacklist(&blacklist_proto);
51 UMA_HISTOGRAM_COUNTS_10000("Suggestions.LocalBlacklistSize",
52 blacklist_proto.urls_size());
55 BlacklistStore::~BlacklistStore() {}
57 bool BlacklistStore::BlacklistUrl(const GURL& url) {
58 if (!url.is_valid()) return false;
60 SuggestionsBlacklist blacklist_proto;
61 LoadBlacklist(&blacklist_proto);
62 std::set<std::string> blacklist_set;
63 PopulateBlacklistSet(blacklist_proto, &blacklist_set);
65 bool success = false;
66 if (blacklist_set.insert(url.spec()).second) {
67 PopulateBlacklistProto(blacklist_set, &blacklist_proto);
68 success = StoreBlacklist(blacklist_proto);
69 } else {
70 // |url| was already in the blacklist.
71 success = true;
74 if (success) {
75 // Update the blacklist time.
76 blacklist_times_[url.spec()] = TimeTicks::Now();
79 return success;
82 bool BlacklistStore::GetTimeUntilReadyForUpload(TimeDelta* delta) {
83 SuggestionsBlacklist blacklist;
84 LoadBlacklist(&blacklist);
85 if (!blacklist.urls_size())
86 return false;
88 // Note: the size is non-negative.
89 if (blacklist_times_.size() < static_cast<size_t>(blacklist.urls_size())) {
90 // A url is not in the timestamp map: it's candidate for upload. This can
91 // happen after a restart. Another (undesired) case when this could happen
92 // is if more than one instance were created.
93 *delta = TimeDelta::FromSeconds(0);
94 return true;
97 // Find the minimum blacklist time. Note: blacklist_times_ is NOT empty since
98 // blacklist is non-empty and blacklist_times_ contains as many items.
99 TimeDelta min_delay = TimeDelta::Max();
100 for (const auto& kv : blacklist_times_) {
101 min_delay = std::min(upload_delay_ - (TimeTicks::Now() - kv.second),
102 min_delay);
104 DCHECK(min_delay != TimeDelta::Max());
105 *delta = std::max(min_delay, TimeDelta::FromSeconds(0));
107 return true;
110 bool BlacklistStore::GetTimeUntilURLReadyForUpload(const GURL& url,
111 TimeDelta* delta) {
112 auto it = blacklist_times_.find(url.spec());
113 if (it != blacklist_times_.end()) {
114 // The url is in the timestamps map.
115 *delta = std::max(upload_delay_ - (TimeTicks::Now() - it->second),
116 TimeDelta::FromSeconds(0));
117 return true;
120 // The url still might be in the blacklist.
121 SuggestionsBlacklist blacklist;
122 LoadBlacklist(&blacklist);
123 for (int i = 0; i < blacklist.urls_size(); ++i) {
124 if (blacklist.urls(i) == url.spec()) {
125 *delta = TimeDelta::FromSeconds(0);
126 return true;
130 return false;
133 bool BlacklistStore::GetCandidateForUpload(GURL* url) {
134 SuggestionsBlacklist blacklist;
135 LoadBlacklist(&blacklist);
137 for (int i = 0; i < blacklist.urls_size(); ++i) {
138 bool is_candidate = true;
139 auto it = blacklist_times_.find(blacklist.urls(i));
140 if (it != blacklist_times_.end() &&
141 TimeTicks::Now() < it->second + upload_delay_) {
142 // URL was added too recently.
143 is_candidate = false;
145 if (is_candidate) {
146 GURL blacklisted(blacklist.urls(i));
147 url->Swap(&blacklisted);
148 return true;
152 return false;
155 bool BlacklistStore::RemoveUrl(const GURL& url) {
156 if (!url.is_valid()) return false;
157 const std::string removal_candidate = url.spec();
159 SuggestionsBlacklist blacklist;
160 LoadBlacklist(&blacklist);
162 bool removed = false;
163 SuggestionsBlacklist updated_blacklist;
164 for (int i = 0; i < blacklist.urls_size(); ++i) {
165 if (blacklist.urls(i) == removal_candidate) {
166 removed = true;
167 } else {
168 updated_blacklist.add_urls(blacklist.urls(i));
172 if (removed && StoreBlacklist(updated_blacklist)) {
173 blacklist_times_.erase(url.spec());
174 return true;
177 return false;
180 void BlacklistStore::FilterSuggestions(SuggestionsProfile* profile) {
181 if (!profile->suggestions_size())
182 return; // Empty profile, nothing to filter.
184 SuggestionsBlacklist blacklist_proto;
185 if (!LoadBlacklist(&blacklist_proto)) {
186 // There was an error loading the blacklist. The blacklist was cleared and
187 // there's nothing to be done about it.
188 return;
190 if (!blacklist_proto.urls_size())
191 return; // Empty blacklist, nothing to filter.
193 std::set<std::string> blacklist_set;
194 PopulateBlacklistSet(blacklist_proto, &blacklist_set);
196 // Populate the filtered suggestions.
197 SuggestionsProfile filtered_profile;
198 for (int i = 0; i < profile->suggestions_size(); ++i) {
199 if (blacklist_set.find(profile->suggestions(i).url()) ==
200 blacklist_set.end()) {
201 // This suggestion is not blacklisted.
202 ChromeSuggestion* suggestion = filtered_profile.add_suggestions();
203 // Note: swapping!
204 suggestion->Swap(profile->mutable_suggestions(i));
208 // Swap |profile| and |filtered_profile|.
209 profile->Swap(&filtered_profile);
212 // static
213 void BlacklistStore::RegisterProfilePrefs(
214 user_prefs::PrefRegistrySyncable* registry) {
215 registry->RegisterStringPref(prefs::kSuggestionsBlacklist, std::string());
219 // Test seam. For simplicity of mock creation.
220 BlacklistStore::BlacklistStore() {
223 bool BlacklistStore::LoadBlacklist(SuggestionsBlacklist* blacklist) {
224 DCHECK(blacklist);
226 const std::string base64_blacklist_data =
227 pref_service_->GetString(prefs::kSuggestionsBlacklist);
228 if (base64_blacklist_data.empty()) {
229 blacklist->Clear();
230 return false;
233 // If the decode process fails, assume the pref value is corrupt and clear it.
234 std::string blacklist_data;
235 if (!base::Base64Decode(base64_blacklist_data, &blacklist_data) ||
236 !blacklist->ParseFromString(blacklist_data)) {
237 VLOG(1) << "Suggestions blacklist data in profile pref is corrupt, "
238 << " clearing it.";
239 blacklist->Clear();
240 ClearBlacklist();
241 return false;
244 return true;
247 bool BlacklistStore::StoreBlacklist(const SuggestionsBlacklist& blacklist) {
248 std::string blacklist_data;
249 if (!blacklist.SerializeToString(&blacklist_data)) return false;
251 std::string base64_blacklist_data;
252 base::Base64Encode(blacklist_data, &base64_blacklist_data);
254 pref_service_->SetString(prefs::kSuggestionsBlacklist, base64_blacklist_data);
255 return true;
258 void BlacklistStore::ClearBlacklist() {
259 pref_service_->ClearPref(prefs::kSuggestionsBlacklist);
262 } // namespace suggestions