OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/search/suggestions/suggestions_service.h" | 5 #include "chrome/browser/search/suggestions/suggestions_service.h" |
6 | 6 |
| 7 #include <sstream> |
| 8 #include <string> |
| 9 |
7 #include "base/memory/scoped_ptr.h" | 10 #include "base/memory/scoped_ptr.h" |
8 #include "base/metrics/field_trial.h" | 11 #include "base/metrics/field_trial.h" |
9 #include "base/metrics/histogram.h" | 12 #include "base/metrics/histogram.h" |
10 #include "base/metrics/sparse_histogram.h" | 13 #include "base/metrics/sparse_histogram.h" |
11 #include "base/strings/string_number_conversions.h" | 14 #include "base/strings/string_number_conversions.h" |
12 #include "base/strings/string_util.h" | 15 #include "base/strings/string_util.h" |
13 #include "base/time/time.h" | 16 #include "base/time/time.h" |
14 #include "chrome/browser/browser_process.h" | 17 #include "chrome/browser/browser_process.h" |
15 #include "chrome/browser/history/history_types.h" | 18 #include "chrome/browser/history/history_types.h" |
16 #include "chrome/browser/metrics/variations/variations_http_header_provider.h" | 19 #include "chrome/browser/metrics/variations/variations_http_header_provider.h" |
17 #include "chrome/browser/profiles/profile.h" | 20 #include "chrome/browser/profiles/profile.h" |
| 21 #include "chrome/browser/search/suggestions/blacklist_store.h" |
18 #include "chrome/browser/search/suggestions/suggestions_store.h" | 22 #include "chrome/browser/search/suggestions/suggestions_store.h" |
19 #include "components/pref_registry/pref_registry_syncable.h" | 23 #include "components/pref_registry/pref_registry_syncable.h" |
20 #include "components/variations/variations_associated_data.h" | 24 #include "components/variations/variations_associated_data.h" |
21 #include "content/public/browser/browser_thread.h" | 25 #include "content/public/browser/browser_thread.h" |
22 #include "net/base/escape.h" | 26 #include "net/base/escape.h" |
23 #include "net/base/load_flags.h" | 27 #include "net/base/load_flags.h" |
24 #include "net/base/net_errors.h" | 28 #include "net/base/net_errors.h" |
| 29 #include "net/base/url_util.h" |
25 #include "net/http/http_response_headers.h" | 30 #include "net/http/http_response_headers.h" |
26 #include "net/http/http_status_code.h" | 31 #include "net/http/http_status_code.h" |
27 #include "net/http/http_util.h" | 32 #include "net/http/http_util.h" |
28 #include "net/url_request/url_fetcher.h" | 33 #include "net/url_request/url_fetcher.h" |
29 #include "net/url_request/url_request_status.h" | 34 #include "net/url_request/url_request_status.h" |
30 #include "url/gurl.h" | 35 #include "url/gurl.h" |
31 | 36 |
32 using base::CancelableClosure; | 37 using base::CancelableClosure; |
33 using content::BrowserThread; | 38 using content::BrowserThread; |
34 | 39 |
(...skipping 15 matching lines...) Expand all Loading... |
50 RESPONSE_STATE_SIZE); | 55 RESPONSE_STATE_SIZE); |
51 } | 56 } |
52 | 57 |
53 // Obtains the experiment parameter under the supplied |key|, or empty string | 58 // Obtains the experiment parameter under the supplied |key|, or empty string |
54 // if the parameter does not exist. | 59 // if the parameter does not exist. |
55 std::string GetExperimentParam(const std::string& key) { | 60 std::string GetExperimentParam(const std::string& key) { |
56 return chrome_variations::GetVariationParamValue(kSuggestionsFieldTrialName, | 61 return chrome_variations::GetVariationParamValue(kSuggestionsFieldTrialName, |
57 key); | 62 key); |
58 } | 63 } |
59 | 64 |
| 65 GURL BuildBlacklistRequestURL(const std::string& blacklist_url_prefix, |
| 66 const GURL& candidate_url) { |
| 67 return GURL(blacklist_url_prefix + |
| 68 net::EscapeQueryParamValue(candidate_url.spec(), true)); |
| 69 } |
| 70 |
60 // Runs each callback in |requestors| on |suggestions|, then deallocates | 71 // Runs each callback in |requestors| on |suggestions|, then deallocates |
61 // |requestors|. | 72 // |requestors|. |
62 void DispatchRequestsAndClear( | 73 void DispatchRequestsAndClear( |
63 const SuggestionsProfile& suggestions, | 74 const SuggestionsProfile& suggestions, |
64 std::vector<SuggestionsService::ResponseCallback>* requestors) { | 75 std::vector<SuggestionsService::ResponseCallback>* requestors) { |
65 std::vector<SuggestionsService::ResponseCallback>::iterator it; | 76 std::vector<SuggestionsService::ResponseCallback>::iterator it; |
66 for (it = requestors->begin(); it != requestors->end(); ++it) { | 77 for (it = requestors->begin(); it != requestors->end(); ++it) { |
67 it->Run(suggestions); | 78 if (!it->is_null()) it->Run(suggestions); |
68 } | 79 } |
69 std::vector<SuggestionsService::ResponseCallback>().swap(*requestors); | 80 std::vector<SuggestionsService::ResponseCallback>().swap(*requestors); |
70 } | 81 } |
71 | 82 |
72 const int kDefaultRequestTimeoutMs = 200; | 83 const int kDefaultRequestTimeoutMs = 200; |
73 | 84 |
| 85 // Default delay used when scheduling a blacklist request. |
| 86 const int kBlacklistDefaultDelaySec = 1; |
| 87 |
| 88 // Multiplier on the delay used when scheduling a blacklist request, in case the |
| 89 // last observed request was unsuccessful. |
| 90 const int kBlacklistBackoffMultiplier = 2; |
| 91 |
| 92 // Maximum valid delay for scheduling a request. Candidate delays larger than |
| 93 // this are rejected. This means the maximum backoff is at least 300 / 2, i.e. |
| 94 // 2.5 minutes. |
| 95 const int kBlacklistMaxDelaySec = 300; // 5 minutes |
| 96 |
74 } // namespace | 97 } // namespace |
75 | 98 |
76 const char kSuggestionsFieldTrialName[] = "ChromeSuggestions"; | 99 const char kSuggestionsFieldTrialName[] = "ChromeSuggestions"; |
77 const char kSuggestionsFieldTrialURLParam[] = "url"; | 100 const char kSuggestionsFieldTrialURLParam[] = "url"; |
78 const char kSuggestionsFieldTrialSuggestionsSuffixParam[] = | 101 const char kSuggestionsFieldTrialCommonParamsParam[] = "common_params"; |
79 "suggestions_suffix"; | 102 const char kSuggestionsFieldTrialBlacklistPathParam[] = "blacklist_path"; |
80 const char kSuggestionsFieldTrialBlacklistSuffixParam[] = "blacklist_suffix"; | 103 const char kSuggestionsFieldTrialBlacklistUrlParam[] = "blacklist_url_param"; |
81 const char kSuggestionsFieldTrialStateParam[] = "state"; | 104 const char kSuggestionsFieldTrialStateParam[] = "state"; |
82 const char kSuggestionsFieldTrialControlParam[] = "control"; | 105 const char kSuggestionsFieldTrialControlParam[] = "control"; |
83 const char kSuggestionsFieldTrialStateEnabled[] = "enabled"; | 106 const char kSuggestionsFieldTrialStateEnabled[] = "enabled"; |
84 const char kSuggestionsFieldTrialTimeoutMs[] = "timeout_ms"; | 107 const char kSuggestionsFieldTrialTimeoutMs[] = "timeout_ms"; |
85 | 108 |
| 109 namespace { |
| 110 |
| 111 std::string GetBlacklistUrlPrefix() { |
| 112 std::stringstream blacklist_url_prefix_stream; |
| 113 blacklist_url_prefix_stream |
| 114 << GetExperimentParam(kSuggestionsFieldTrialURLParam) |
| 115 << GetExperimentParam(kSuggestionsFieldTrialBlacklistPathParam) << "?" |
| 116 << GetExperimentParam(kSuggestionsFieldTrialCommonParamsParam) << "&" |
| 117 << GetExperimentParam(kSuggestionsFieldTrialBlacklistUrlParam) << "="; |
| 118 return blacklist_url_prefix_stream.str(); |
| 119 } |
| 120 |
| 121 } // namespace |
| 122 |
86 SuggestionsService::SuggestionsService( | 123 SuggestionsService::SuggestionsService( |
87 Profile* profile, scoped_ptr<SuggestionsStore> suggestions_store, | 124 Profile* profile, scoped_ptr<SuggestionsStore> suggestions_store, |
88 scoped_ptr<ThumbnailManager> thumbnail_manager) | 125 scoped_ptr<ThumbnailManager> thumbnail_manager, |
| 126 scoped_ptr<BlacklistStore> blacklist_store) |
89 : suggestions_store_(suggestions_store.Pass()), | 127 : suggestions_store_(suggestions_store.Pass()), |
| 128 blacklist_store_(blacklist_store.Pass()), |
90 thumbnail_manager_(thumbnail_manager.Pass()), | 129 thumbnail_manager_(thumbnail_manager.Pass()), |
91 profile_(profile), | 130 profile_(profile), |
| 131 blacklist_delay_sec_(kBlacklistDefaultDelaySec), |
92 weak_ptr_factory_(this), | 132 weak_ptr_factory_(this), |
93 request_timeout_ms_(kDefaultRequestTimeoutMs) { | 133 request_timeout_ms_(kDefaultRequestTimeoutMs) { |
94 // Obtain various parameters from Variations. | 134 // Obtain various parameters from Variations. |
95 suggestions_url_ = | 135 suggestions_url_ = |
96 GURL(GetExperimentParam(kSuggestionsFieldTrialURLParam) + | 136 GURL(GetExperimentParam(kSuggestionsFieldTrialURLParam) + "?" + |
97 GetExperimentParam(kSuggestionsFieldTrialSuggestionsSuffixParam)); | 137 GetExperimentParam(kSuggestionsFieldTrialCommonParamsParam)); |
98 blacklist_url_prefix_ = | 138 blacklist_url_prefix_ = GetBlacklistUrlPrefix(); |
99 GetExperimentParam(kSuggestionsFieldTrialURLParam) + | |
100 GetExperimentParam(kSuggestionsFieldTrialBlacklistSuffixParam); | |
101 std::string timeout = GetExperimentParam(kSuggestionsFieldTrialTimeoutMs); | 139 std::string timeout = GetExperimentParam(kSuggestionsFieldTrialTimeoutMs); |
102 int temp_timeout; | 140 int temp_timeout; |
103 if (!timeout.empty() && base::StringToInt(timeout, &temp_timeout)) { | 141 if (!timeout.empty() && base::StringToInt(timeout, &temp_timeout)) { |
104 request_timeout_ms_ = temp_timeout; | 142 request_timeout_ms_ = temp_timeout; |
105 } | 143 } |
106 } | 144 } |
107 | 145 |
108 SuggestionsService::~SuggestionsService() {} | 146 SuggestionsService::~SuggestionsService() {} |
109 | 147 |
110 // static | 148 // static |
(...skipping 28 matching lines...) Expand all Loading... |
139 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 177 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
140 if (pending_request_.get()) { | 178 if (pending_request_.get()) { |
141 // Request already exists, so just add requestor to queue. | 179 // Request already exists, so just add requestor to queue. |
142 waiting_requestors_.push_back(callback); | 180 waiting_requestors_.push_back(callback); |
143 return; | 181 return; |
144 } | 182 } |
145 | 183 |
146 // Form new request. | 184 // Form new request. |
147 DCHECK(waiting_requestors_.empty()); | 185 DCHECK(waiting_requestors_.empty()); |
148 waiting_requestors_.push_back(callback); | 186 waiting_requestors_.push_back(callback); |
149 | 187 IssueRequest(suggestions_url_); |
150 pending_request_.reset(CreateSuggestionsRequest(suggestions_url_)); | |
151 pending_request_->Start(); | |
152 last_request_started_time_ = base::TimeTicks::Now(); | |
153 } | 188 } |
154 | 189 |
155 void SuggestionsService::GetPageThumbnail( | 190 void SuggestionsService::GetPageThumbnail( |
156 const GURL& url, | 191 const GURL& url, |
157 base::Callback<void(const GURL&, const SkBitmap*)> callback) { | 192 base::Callback<void(const GURL&, const SkBitmap*)> callback) { |
158 thumbnail_manager_->GetPageThumbnail(url, callback); | 193 thumbnail_manager_->GetPageThumbnail(url, callback); |
159 } | 194 } |
160 | 195 |
161 void SuggestionsService::BlacklistURL( | 196 void SuggestionsService::BlacklistURL( |
162 const GURL& candidate_url, SuggestionsService::ResponseCallback callback) { | 197 const GURL& candidate_url, |
| 198 const SuggestionsService::ResponseCallback& callback) { |
163 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 199 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
164 waiting_requestors_.push_back(callback); | 200 waiting_requestors_.push_back(callback); |
165 | 201 |
166 if (pending_request_.get()) { | 202 // Blacklist locally, for immediate effect. |
167 if (IsBlacklistRequest(pending_request_.get())) { | 203 if (!blacklist_store_->BlacklistUrl(candidate_url)) { |
168 // Pending request is a blacklist request. Silently drop the new blacklist | 204 DVLOG(1) << "Failed blacklisting attempt."; |
169 // request. TODO - handle this case. | 205 return; |
170 return; | |
171 } else { | |
172 // Pending request is not a blacklist request - cancel it and go on to | |
173 // issuing a blacklist request. Also ensure the timeout closure does not | |
174 // run; instead we'll wait for the updated suggestions before servicing | |
175 // requestors. | |
176 pending_request_.reset(NULL); | |
177 pending_timeout_closure_.reset(NULL); | |
178 } | |
179 } | 206 } |
180 | 207 |
181 // Send blacklisting request. | 208 // If there's an ongoing request, let it complete. |
182 // TODO(manzagop): make this a PUT request instead of a GET request. | 209 if (pending_request_.get()) return; |
183 GURL url(blacklist_url_prefix_ + | 210 |
184 net::EscapeQueryParamValue(candidate_url.spec(), true)); | 211 IssueRequest(BuildBlacklistRequestURL(blacklist_url_prefix_, candidate_url)); |
185 pending_request_.reset(CreateSuggestionsRequest(url)); | 212 } |
186 pending_request_->Start(); | 213 |
187 last_request_started_time_ = base::TimeTicks::Now(); | 214 // static |
| 215 bool SuggestionsService::GetBlacklistedUrl(const net::URLFetcher& request, |
| 216 GURL* url) { |
| 217 bool is_blacklist_request = StartsWithASCII(request.GetOriginalURL().spec(), |
| 218 GetBlacklistUrlPrefix(), true); |
| 219 if (!is_blacklist_request) return false; |
| 220 |
| 221 // Extract the blacklisted URL from the blacklist request. |
| 222 std::string blacklisted; |
| 223 if (!net::GetValueForKeyInQuery( |
| 224 request.GetOriginalURL(), |
| 225 GetExperimentParam(kSuggestionsFieldTrialBlacklistUrlParam), |
| 226 &blacklisted)) |
| 227 return false; |
| 228 |
| 229 GURL blacklisted_url(blacklisted); |
| 230 blacklisted_url.Swap(url); |
| 231 return true; |
188 } | 232 } |
189 | 233 |
190 // static | 234 // static |
191 void SuggestionsService::RegisterProfilePrefs( | 235 void SuggestionsService::RegisterProfilePrefs( |
192 user_prefs::PrefRegistrySyncable* registry) { | 236 user_prefs::PrefRegistrySyncable* registry) { |
193 SuggestionsStore::RegisterProfilePrefs(registry); | 237 SuggestionsStore::RegisterProfilePrefs(registry); |
| 238 BlacklistStore::RegisterProfilePrefs(registry); |
| 239 } |
| 240 |
| 241 void SuggestionsService::IssueRequest(const GURL& url) { |
| 242 pending_request_.reset(CreateSuggestionsRequest(url)); |
| 243 pending_request_->Start(); |
| 244 last_request_started_time_ = base::TimeTicks::Now(); |
| 245 } |
| 246 |
| 247 net::URLFetcher* SuggestionsService::CreateSuggestionsRequest(const GURL& url) { |
| 248 net::URLFetcher* request = |
| 249 net::URLFetcher::Create(0, url, net::URLFetcher::GET, this); |
| 250 request->SetLoadFlags(net::LOAD_DISABLE_CACHE); |
| 251 request->SetRequestContext(profile_->GetRequestContext()); |
| 252 // Add Chrome experiment state to the request headers. |
| 253 net::HttpRequestHeaders headers; |
| 254 chrome_variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders( |
| 255 request->GetOriginalURL(), profile_->IsOffTheRecord(), false, &headers); |
| 256 request->SetExtraRequestHeaders(headers.ToString()); |
| 257 return request; |
194 } | 258 } |
195 | 259 |
196 void SuggestionsService::OnRequestTimeout() { | 260 void SuggestionsService::OnRequestTimeout() { |
197 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 261 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
198 ServeFromCache(); | 262 ServeFromCache(); |
199 } | 263 } |
200 | 264 |
201 void SuggestionsService::OnURLFetchComplete(const net::URLFetcher* source) { | 265 void SuggestionsService::OnURLFetchComplete(const net::URLFetcher* source) { |
202 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 266 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
203 DCHECK_EQ(pending_request_.get(), source); | 267 DCHECK_EQ(pending_request_.get(), source); |
204 | 268 |
205 // We no longer need the timeout closure. Delete it whether or not it has run | 269 // We no longer need the timeout closure. Delete it whether or not it has run. |
206 // (if it hasn't, this cancels it). | 270 // If it hasn't, this cancels it. |
207 pending_timeout_closure_.reset(); | 271 pending_timeout_closure_.reset(); |
208 | 272 |
209 // The fetcher will be deleted when the request is handled. | 273 // The fetcher will be deleted when the request is handled. |
210 scoped_ptr<const net::URLFetcher> request(pending_request_.release()); | 274 scoped_ptr<const net::URLFetcher> request(pending_request_.release()); |
211 const net::URLRequestStatus& request_status = request->GetStatus(); | 275 const net::URLRequestStatus& request_status = request->GetStatus(); |
212 if (request_status.status() != net::URLRequestStatus::SUCCESS) { | 276 if (request_status.status() != net::URLRequestStatus::SUCCESS) { |
213 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FailedRequestErrorCode", | 277 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FailedRequestErrorCode", |
214 -request_status.error()); | 278 -request_status.error()); |
215 DVLOG(1) << "Suggestions server request failed with error: " | 279 DVLOG(1) << "Suggestions server request failed with error: " |
216 << request_status.error() << ": " | 280 << request_status.error() << ": " |
217 << net::ErrorToString(request_status.error()); | 281 << net::ErrorToString(request_status.error()); |
218 // Dispatch the cached profile on error. | 282 // Dispatch the cached profile on error. |
219 ServeFromCache(); | 283 ServeFromCache(); |
| 284 ScheduleBlacklistUpload(false); |
220 return; | 285 return; |
221 } | 286 } |
222 | 287 |
223 // Log the response code. | 288 // Log the response code. |
224 const int response_code = request->GetResponseCode(); | 289 const int response_code = request->GetResponseCode(); |
225 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FetchResponseCode", response_code); | 290 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FetchResponseCode", response_code); |
226 if (response_code != net::HTTP_OK) { | 291 if (response_code != net::HTTP_OK) { |
227 // Aggressively clear the store. | 292 // Aggressively clear the store. |
228 suggestions_store_->ClearSuggestions(); | 293 suggestions_store_->ClearSuggestions(); |
229 DispatchRequestsAndClear(SuggestionsProfile(), &waiting_requestors_); | 294 DispatchRequestsAndClear(SuggestionsProfile(), &waiting_requestors_); |
| 295 ScheduleBlacklistUpload(false); |
230 return; | 296 return; |
231 } | 297 } |
232 | 298 |
233 const base::TimeDelta latency = | 299 const base::TimeDelta latency = |
234 base::TimeTicks::Now() - last_request_started_time_; | 300 base::TimeTicks::Now() - last_request_started_time_; |
235 UMA_HISTOGRAM_MEDIUM_TIMES("Suggestions.FetchSuccessLatency", latency); | 301 UMA_HISTOGRAM_MEDIUM_TIMES("Suggestions.FetchSuccessLatency", latency); |
236 | 302 |
| 303 // Handle a successful blacklisting. |
| 304 GURL blacklisted_url; |
| 305 if (GetBlacklistedUrl(*source, &blacklisted_url)) { |
| 306 blacklist_store_->RemoveUrl(blacklisted_url); |
| 307 } |
| 308 |
237 std::string suggestions_data; | 309 std::string suggestions_data; |
238 bool success = request->GetResponseAsString(&suggestions_data); | 310 bool success = request->GetResponseAsString(&suggestions_data); |
239 DCHECK(success); | 311 DCHECK(success); |
240 | 312 |
241 // Compute suggestions, and dispatch then to requestors. On error still | 313 // Compute suggestions, and dispatch then to requestors. On error still |
242 // dispatch empty suggestions. | 314 // dispatch empty suggestions. |
243 SuggestionsProfile suggestions; | 315 SuggestionsProfile suggestions; |
244 if (suggestions_data.empty()) { | 316 if (suggestions_data.empty()) { |
245 LogResponseState(RESPONSE_EMPTY); | 317 LogResponseState(RESPONSE_EMPTY); |
246 suggestions_store_->ClearSuggestions(); | 318 suggestions_store_->ClearSuggestions(); |
247 } else if (suggestions.ParseFromString(suggestions_data)) { | 319 } else if (suggestions.ParseFromString(suggestions_data)) { |
248 LogResponseState(RESPONSE_VALID); | 320 LogResponseState(RESPONSE_VALID); |
249 thumbnail_manager_->InitializeThumbnailMap(suggestions); | 321 thumbnail_manager_->InitializeThumbnailMap(suggestions); |
250 suggestions_store_->StoreSuggestions(suggestions); | 322 suggestions_store_->StoreSuggestions(suggestions); |
251 } else { | 323 } else { |
252 LogResponseState(RESPONSE_INVALID); | 324 LogResponseState(RESPONSE_INVALID); |
253 suggestions_store_->LoadSuggestions(&suggestions); | 325 suggestions_store_->LoadSuggestions(&suggestions); |
254 } | 326 } |
255 | 327 |
256 DispatchRequestsAndClear(suggestions, &waiting_requestors_); | 328 FilterAndServe(&suggestions); |
| 329 ScheduleBlacklistUpload(true); |
257 } | 330 } |
258 | 331 |
259 void SuggestionsService::Shutdown() { | 332 void SuggestionsService::Shutdown() { |
260 // Cancel pending request and timeout closure, then serve existing requestors | 333 // Cancel pending request and timeout closure, then serve existing requestors |
261 // from cache. | 334 // from cache. |
262 pending_request_.reset(NULL); | 335 pending_request_.reset(NULL); |
263 pending_timeout_closure_.reset(NULL); | 336 pending_timeout_closure_.reset(NULL); |
264 ServeFromCache(); | 337 ServeFromCache(); |
265 } | 338 } |
266 | 339 |
267 bool SuggestionsService::IsBlacklistRequest(net::URLFetcher* request) const { | |
268 DCHECK(request); | |
269 return StartsWithASCII(request->GetOriginalURL().spec(), | |
270 blacklist_url_prefix_, true); | |
271 } | |
272 | |
273 net::URLFetcher* SuggestionsService::CreateSuggestionsRequest(const GURL& url) { | |
274 net::URLFetcher* request = | |
275 net::URLFetcher::Create(0, url, net::URLFetcher::GET, this); | |
276 request->SetLoadFlags(net::LOAD_DISABLE_CACHE); | |
277 request->SetRequestContext(profile_->GetRequestContext()); | |
278 // Add Chrome experiment state to the request headers. | |
279 net::HttpRequestHeaders headers; | |
280 chrome_variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders( | |
281 request->GetOriginalURL(), profile_->IsOffTheRecord(), false, &headers); | |
282 request->SetExtraRequestHeaders(headers.ToString()); | |
283 return request; | |
284 } | |
285 | |
286 void SuggestionsService::ServeFromCache() { | 340 void SuggestionsService::ServeFromCache() { |
287 SuggestionsProfile suggestions; | 341 SuggestionsProfile suggestions; |
288 suggestions_store_->LoadSuggestions(&suggestions); | 342 suggestions_store_->LoadSuggestions(&suggestions); |
289 DispatchRequestsAndClear(suggestions, &waiting_requestors_); | 343 FilterAndServe(&suggestions); |
| 344 } |
| 345 |
| 346 void SuggestionsService::FilterAndServe(SuggestionsProfile* suggestions) { |
| 347 blacklist_store_->FilterSuggestions(suggestions); |
| 348 DispatchRequestsAndClear(*suggestions, &waiting_requestors_); |
| 349 } |
| 350 |
| 351 void SuggestionsService::ScheduleBlacklistUpload(bool last_request_successful) { |
| 352 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 353 |
| 354 UpdateBlacklistDelay(last_request_successful); |
| 355 |
| 356 // Schedule a blacklist upload task. |
| 357 GURL blacklist_url; |
| 358 if (blacklist_store_->GetFirstUrlFromBlacklist(&blacklist_url)) { |
| 359 base::Closure blacklist_cb = |
| 360 base::Bind(&SuggestionsService::UploadOneFromBlacklist, |
| 361 weak_ptr_factory_.GetWeakPtr()); |
| 362 BrowserThread::PostDelayedTask( |
| 363 BrowserThread::UI, FROM_HERE, blacklist_cb, |
| 364 base::TimeDelta::FromSeconds(blacklist_delay_sec_)); |
| 365 } |
| 366 } |
| 367 |
| 368 void SuggestionsService::UploadOneFromBlacklist() { |
| 369 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 370 |
| 371 // If there's an ongoing request, let it complete. |
| 372 if (pending_request_.get()) return; |
| 373 |
| 374 GURL blacklist_url; |
| 375 if (!blacklist_store_->GetFirstUrlFromBlacklist(&blacklist_url)) |
| 376 return; // Local blacklist is empty. |
| 377 |
| 378 // Send blacklisting request. |
| 379 IssueRequest(BuildBlacklistRequestURL(blacklist_url_prefix_, blacklist_url)); |
| 380 } |
| 381 |
| 382 void SuggestionsService::UpdateBlacklistDelay(bool last_request_successful) { |
| 383 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 384 |
| 385 if (last_request_successful) { |
| 386 blacklist_delay_sec_ = kBlacklistDefaultDelaySec; |
| 387 } else { |
| 388 int candidate_delay = blacklist_delay_sec_ * kBlacklistBackoffMultiplier; |
| 389 if (candidate_delay < kBlacklistMaxDelaySec) |
| 390 blacklist_delay_sec_ = candidate_delay; |
| 391 } |
290 } | 392 } |
291 | 393 |
292 } // namespace suggestions | 394 } // namespace suggestions |
OLD | NEW |