Chromium Code Reviews| 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 unsigned int kBlacklistDefaultDelaySec = 1; | |
|
Alexei Svitkine (slow)
2014/06/20 14:41:41
Nit: Generally, we use size_t for unsigned ints in
manzagop (departed)
2014/06/20 15:13:53
Done.
| |
| 87 | |
| 88 // Multiplier on the delay used when scheduling a blacklist request, in case the | |
| 89 // last observed request was unsuccessful. | |
| 90 const unsigned 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 unsigned 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, |
| 125 scoped_ptr<BlacklistStore> blacklist_store) | |
| 88 : suggestions_store_(suggestions_store.Pass()), | 126 : suggestions_store_(suggestions_store.Pass()), |
| 127 blacklist_store_(blacklist_store.Pass()), | |
| 89 thumbnail_manager_(new ThumbnailManager(profile)), | 128 thumbnail_manager_(new ThumbnailManager(profile)), |
| 90 profile_(profile), | 129 profile_(profile), |
| 130 blacklist_delay_sec_(kBlacklistDefaultDelaySec), | |
| 91 weak_ptr_factory_(this), | 131 weak_ptr_factory_(this), |
| 92 request_timeout_ms_(kDefaultRequestTimeoutMs) { | 132 request_timeout_ms_(kDefaultRequestTimeoutMs) { |
| 93 // Obtain various parameters from Variations. | 133 // Obtain various parameters from Variations. |
| 94 suggestions_url_ = | 134 suggestions_url_ = |
| 95 GURL(GetExperimentParam(kSuggestionsFieldTrialURLParam) + | 135 GURL(GetExperimentParam(kSuggestionsFieldTrialURLParam) + "?" + |
| 96 GetExperimentParam(kSuggestionsFieldTrialSuggestionsSuffixParam)); | 136 GetExperimentParam(kSuggestionsFieldTrialCommonParamsParam)); |
| 97 blacklist_url_prefix_ = | 137 blacklist_url_prefix_ = GetBlacklistUrlPrefix(); |
| 98 GetExperimentParam(kSuggestionsFieldTrialURLParam) + | |
| 99 GetExperimentParam(kSuggestionsFieldTrialBlacklistSuffixParam); | |
| 100 std::string timeout = GetExperimentParam(kSuggestionsFieldTrialTimeoutMs); | 138 std::string timeout = GetExperimentParam(kSuggestionsFieldTrialTimeoutMs); |
| 101 int temp_timeout; | 139 int temp_timeout; |
| 102 if (!timeout.empty() && base::StringToInt(timeout, &temp_timeout)) { | 140 if (!timeout.empty() && base::StringToInt(timeout, &temp_timeout)) { |
| 103 request_timeout_ms_ = temp_timeout; | 141 request_timeout_ms_ = temp_timeout; |
| 104 } | 142 } |
| 105 } | 143 } |
| 106 | 144 |
| 107 SuggestionsService::~SuggestionsService() {} | 145 SuggestionsService::~SuggestionsService() {} |
| 108 | 146 |
| 109 // static | 147 // static |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 138 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 176 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 139 if (pending_request_.get()) { | 177 if (pending_request_.get()) { |
| 140 // Request already exists, so just add requestor to queue. | 178 // Request already exists, so just add requestor to queue. |
| 141 waiting_requestors_.push_back(callback); | 179 waiting_requestors_.push_back(callback); |
| 142 return; | 180 return; |
| 143 } | 181 } |
| 144 | 182 |
| 145 // Form new request. | 183 // Form new request. |
| 146 DCHECK(waiting_requestors_.empty()); | 184 DCHECK(waiting_requestors_.empty()); |
| 147 waiting_requestors_.push_back(callback); | 185 waiting_requestors_.push_back(callback); |
| 148 | 186 IssueRequest(suggestions_url_); |
| 149 pending_request_.reset(CreateSuggestionsRequest(suggestions_url_)); | |
| 150 pending_request_->Start(); | |
| 151 last_request_started_time_ = base::TimeTicks::Now(); | |
| 152 } | 187 } |
| 153 | 188 |
| 154 void SuggestionsService::GetPageThumbnail( | 189 void SuggestionsService::GetPageThumbnail( |
| 155 const GURL& url, | 190 const GURL& url, |
| 156 base::Callback<void(const GURL&, const SkBitmap*)> callback) { | 191 base::Callback<void(const GURL&, const SkBitmap*)> callback) { |
| 157 thumbnail_manager_->GetPageThumbnail(url, callback); | 192 thumbnail_manager_->GetPageThumbnail(url, callback); |
| 158 } | 193 } |
| 159 | 194 |
| 160 void SuggestionsService::BlacklistURL( | 195 void SuggestionsService::BlacklistURL( |
| 161 const GURL& candidate_url, SuggestionsService::ResponseCallback callback) { | 196 const GURL& candidate_url, |
| 197 const SuggestionsService::ResponseCallback& callback) { | |
| 162 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 198 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 163 waiting_requestors_.push_back(callback); | 199 waiting_requestors_.push_back(callback); |
| 164 | 200 |
| 165 if (pending_request_.get()) { | 201 // Blacklist locally, for immediate effect. |
| 166 if (IsBlacklistRequest(pending_request_.get())) { | 202 if (!blacklist_store_->BlacklistUrl(candidate_url)) { |
| 167 // Pending request is a blacklist request. Silently drop the new blacklist | 203 DVLOG(1) << "Failed blacklisting attempt."; |
| 168 // request. TODO - handle this case. | 204 return; |
| 169 return; | |
| 170 } else { | |
| 171 // Pending request is not a blacklist request - cancel it and go on to | |
| 172 // issuing a blacklist request. Also ensure the timeout closure does not | |
| 173 // run; instead we'll wait for the updated suggestions before servicing | |
| 174 // requestors. | |
| 175 pending_request_.reset(NULL); | |
| 176 pending_timeout_closure_.reset(NULL); | |
| 177 } | |
| 178 } | 205 } |
| 179 | 206 |
| 180 // Send blacklisting request. | 207 // If there's an ongoing request, let it complete. |
| 181 // TODO(manzagop): make this a PUT request instead of a GET request. | 208 if (pending_request_.get()) return; |
| 182 GURL url(blacklist_url_prefix_ + | 209 |
| 183 net::EscapeQueryParamValue(candidate_url.spec(), true)); | 210 IssueRequest(BuildBlacklistRequestURL(blacklist_url_prefix_, candidate_url)); |
| 184 pending_request_.reset(CreateSuggestionsRequest(url)); | 211 } |
| 185 pending_request_->Start(); | 212 |
| 186 last_request_started_time_ = base::TimeTicks::Now(); | 213 // static |
| 214 bool SuggestionsService::GetBlacklistedUrl(const net::URLFetcher& request, | |
| 215 GURL* url) { | |
| 216 bool is_blacklist_request = StartsWithASCII(request.GetOriginalURL().spec(), | |
| 217 GetBlacklistUrlPrefix(), true); | |
| 218 if (!is_blacklist_request) return false; | |
| 219 | |
| 220 // Extract the blacklisted URL from the blacklist request. | |
| 221 std::string blacklisted; | |
| 222 if (!net::GetValueForKeyInQuery( | |
| 223 request.GetOriginalURL(), | |
| 224 GetExperimentParam(kSuggestionsFieldTrialBlacklistUrlParam), | |
| 225 &blacklisted)) | |
| 226 return false; | |
| 227 | |
| 228 GURL blacklisted_url(blacklisted); | |
| 229 blacklisted_url.Swap(url); | |
| 230 return true; | |
| 187 } | 231 } |
| 188 | 232 |
| 189 // static | 233 // static |
| 190 void SuggestionsService::RegisterProfilePrefs( | 234 void SuggestionsService::RegisterProfilePrefs( |
| 191 user_prefs::PrefRegistrySyncable* registry) { | 235 user_prefs::PrefRegistrySyncable* registry) { |
| 192 SuggestionsStore::RegisterProfilePrefs(registry); | 236 SuggestionsStore::RegisterProfilePrefs(registry); |
| 237 BlacklistStore::RegisterProfilePrefs(registry); | |
| 238 } | |
| 239 | |
| 240 void SuggestionsService::IssueRequest(const GURL& url) { | |
| 241 pending_request_.reset(CreateSuggestionsRequest(url)); | |
| 242 pending_request_->Start(); | |
| 243 last_request_started_time_ = base::TimeTicks::Now(); | |
| 244 } | |
| 245 | |
| 246 net::URLFetcher* SuggestionsService::CreateSuggestionsRequest(const GURL& url) { | |
| 247 net::URLFetcher* request = | |
| 248 net::URLFetcher::Create(0, url, net::URLFetcher::GET, this); | |
| 249 request->SetLoadFlags(net::LOAD_DISABLE_CACHE); | |
| 250 request->SetRequestContext(profile_->GetRequestContext()); | |
| 251 // Add Chrome experiment state to the request headers. | |
| 252 net::HttpRequestHeaders headers; | |
| 253 chrome_variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders( | |
| 254 request->GetOriginalURL(), profile_->IsOffTheRecord(), false, &headers); | |
| 255 request->SetExtraRequestHeaders(headers.ToString()); | |
| 256 return request; | |
| 193 } | 257 } |
| 194 | 258 |
| 195 void SuggestionsService::OnRequestTimeout() { | 259 void SuggestionsService::OnRequestTimeout() { |
| 196 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 260 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 197 ServeFromCache(); | 261 ServeFromCache(); |
| 198 } | 262 } |
| 199 | 263 |
| 200 void SuggestionsService::OnURLFetchComplete(const net::URLFetcher* source) { | 264 void SuggestionsService::OnURLFetchComplete(const net::URLFetcher* source) { |
| 201 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 265 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 202 DCHECK_EQ(pending_request_.get(), source); | 266 DCHECK_EQ(pending_request_.get(), source); |
| 203 | 267 |
| 204 // We no longer need the timeout closure. Delete it whether or not it has run | 268 // We no longer need the timeout closure. Delete it whether or not it has run. |
| 205 // (if it hasn't, this cancels it). | 269 // If it hasn't, this cancels it. |
| 206 pending_timeout_closure_.reset(); | 270 pending_timeout_closure_.reset(); |
| 207 | 271 |
| 208 // The fetcher will be deleted when the request is handled. | 272 // The fetcher will be deleted when the request is handled. |
| 209 scoped_ptr<const net::URLFetcher> request(pending_request_.release()); | 273 scoped_ptr<const net::URLFetcher> request(pending_request_.release()); |
| 210 const net::URLRequestStatus& request_status = request->GetStatus(); | 274 const net::URLRequestStatus& request_status = request->GetStatus(); |
| 211 if (request_status.status() != net::URLRequestStatus::SUCCESS) { | 275 if (request_status.status() != net::URLRequestStatus::SUCCESS) { |
| 212 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FailedRequestErrorCode", | 276 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FailedRequestErrorCode", |
| 213 -request_status.error()); | 277 -request_status.error()); |
| 214 DVLOG(1) << "Suggestions server request failed with error: " | 278 DVLOG(1) << "Suggestions server request failed with error: " |
| 215 << request_status.error() << ": " | 279 << request_status.error() << ": " |
| 216 << net::ErrorToString(request_status.error()); | 280 << net::ErrorToString(request_status.error()); |
| 217 // Dispatch the cached profile on error. | 281 // Dispatch the cached profile on error. |
| 218 ServeFromCache(); | 282 ServeFromCache(); |
| 283 ScheduleBlacklistUpload(false); | |
| 219 return; | 284 return; |
| 220 } | 285 } |
| 221 | 286 |
| 222 // Log the response code. | 287 // Log the response code. |
| 223 const int response_code = request->GetResponseCode(); | 288 const int response_code = request->GetResponseCode(); |
| 224 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FetchResponseCode", response_code); | 289 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FetchResponseCode", response_code); |
| 225 if (response_code != net::HTTP_OK) { | 290 if (response_code != net::HTTP_OK) { |
| 226 // Aggressively clear the store. | 291 // Aggressively clear the store. |
| 227 suggestions_store_->ClearSuggestions(); | 292 suggestions_store_->ClearSuggestions(); |
| 228 DispatchRequestsAndClear(SuggestionsProfile(), &waiting_requestors_); | 293 DispatchRequestsAndClear(SuggestionsProfile(), &waiting_requestors_); |
| 294 ScheduleBlacklistUpload(false); | |
| 229 return; | 295 return; |
| 230 } | 296 } |
| 231 | 297 |
| 232 const base::TimeDelta latency = | 298 const base::TimeDelta latency = |
| 233 base::TimeTicks::Now() - last_request_started_time_; | 299 base::TimeTicks::Now() - last_request_started_time_; |
| 234 UMA_HISTOGRAM_MEDIUM_TIMES("Suggestions.FetchSuccessLatency", latency); | 300 UMA_HISTOGRAM_MEDIUM_TIMES("Suggestions.FetchSuccessLatency", latency); |
| 235 | 301 |
| 302 // Handle a successful blacklisting. | |
| 303 GURL blacklisted_url; | |
| 304 if (GetBlacklistedUrl(*source, &blacklisted_url)) { | |
| 305 blacklist_store_->RemoveUrl(blacklisted_url); | |
| 306 } | |
| 307 | |
| 236 std::string suggestions_data; | 308 std::string suggestions_data; |
| 237 bool success = request->GetResponseAsString(&suggestions_data); | 309 bool success = request->GetResponseAsString(&suggestions_data); |
| 238 DCHECK(success); | 310 DCHECK(success); |
| 239 | 311 |
| 240 // Compute suggestions, and dispatch then to requestors. On error still | 312 // Compute suggestions, and dispatch then to requestors. On error still |
| 241 // dispatch empty suggestions. | 313 // dispatch empty suggestions. |
| 242 SuggestionsProfile suggestions; | 314 SuggestionsProfile suggestions; |
| 243 if (suggestions_data.empty()) { | 315 if (suggestions_data.empty()) { |
| 244 LogResponseState(RESPONSE_EMPTY); | 316 LogResponseState(RESPONSE_EMPTY); |
| 245 suggestions_store_->ClearSuggestions(); | 317 suggestions_store_->ClearSuggestions(); |
| 246 } else if (suggestions.ParseFromString(suggestions_data)) { | 318 } else if (suggestions.ParseFromString(suggestions_data)) { |
| 247 LogResponseState(RESPONSE_VALID); | 319 LogResponseState(RESPONSE_VALID); |
| 248 thumbnail_manager_->InitializeThumbnailMap(suggestions); | 320 thumbnail_manager_->InitializeThumbnailMap(suggestions); |
| 249 suggestions_store_->StoreSuggestions(suggestions); | 321 suggestions_store_->StoreSuggestions(suggestions); |
| 250 } else { | 322 } else { |
| 251 LogResponseState(RESPONSE_INVALID); | 323 LogResponseState(RESPONSE_INVALID); |
| 252 suggestions_store_->LoadSuggestions(&suggestions); | 324 suggestions_store_->LoadSuggestions(&suggestions); |
| 253 } | 325 } |
| 254 | 326 |
| 255 DispatchRequestsAndClear(suggestions, &waiting_requestors_); | 327 FilterAndServe(&suggestions); |
| 328 ScheduleBlacklistUpload(true); | |
| 256 } | 329 } |
| 257 | 330 |
| 258 void SuggestionsService::Shutdown() { | 331 void SuggestionsService::Shutdown() { |
| 259 // Cancel pending request and timeout closure, then serve existing requestors | 332 // Cancel pending request and timeout closure, then serve existing requestors |
| 260 // from cache. | 333 // from cache. |
| 261 pending_request_.reset(NULL); | 334 pending_request_.reset(NULL); |
| 262 pending_timeout_closure_.reset(NULL); | 335 pending_timeout_closure_.reset(NULL); |
| 263 ServeFromCache(); | 336 ServeFromCache(); |
| 264 } | 337 } |
| 265 | 338 |
| 266 bool SuggestionsService::IsBlacklistRequest(net::URLFetcher* request) const { | |
| 267 DCHECK(request); | |
| 268 return StartsWithASCII(request->GetOriginalURL().spec(), | |
| 269 blacklist_url_prefix_, true); | |
| 270 } | |
| 271 | |
| 272 net::URLFetcher* SuggestionsService::CreateSuggestionsRequest(const GURL& url) { | |
| 273 net::URLFetcher* request = | |
| 274 net::URLFetcher::Create(0, url, net::URLFetcher::GET, this); | |
| 275 request->SetLoadFlags(net::LOAD_DISABLE_CACHE); | |
| 276 request->SetRequestContext(profile_->GetRequestContext()); | |
| 277 // Add Chrome experiment state to the request headers. | |
| 278 net::HttpRequestHeaders headers; | |
| 279 chrome_variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders( | |
| 280 request->GetOriginalURL(), profile_->IsOffTheRecord(), false, &headers); | |
| 281 request->SetExtraRequestHeaders(headers.ToString()); | |
| 282 return request; | |
| 283 } | |
| 284 | |
| 285 void SuggestionsService::ServeFromCache() { | 339 void SuggestionsService::ServeFromCache() { |
| 286 SuggestionsProfile suggestions; | 340 SuggestionsProfile suggestions; |
| 287 suggestions_store_->LoadSuggestions(&suggestions); | 341 suggestions_store_->LoadSuggestions(&suggestions); |
| 288 DispatchRequestsAndClear(suggestions, &waiting_requestors_); | 342 FilterAndServe(&suggestions); |
| 343 } | |
| 344 | |
| 345 void SuggestionsService::FilterAndServe(SuggestionsProfile* suggestions) { | |
| 346 blacklist_store_->FilterSuggestions(suggestions); | |
| 347 DispatchRequestsAndClear(*suggestions, &waiting_requestors_); | |
| 348 } | |
| 349 | |
| 350 void SuggestionsService::ScheduleBlacklistUpload(bool last_request_successful) { | |
| 351 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 352 | |
| 353 UpdateBlacklistDelay(last_request_successful); | |
| 354 | |
| 355 // Schedule a blacklist upload task. | |
| 356 GURL blacklist_url; | |
| 357 if (blacklist_store_->GetFirstUrlFromBlacklist(&blacklist_url)) { | |
| 358 base::Closure blacklist_cb = | |
| 359 base::Bind(&SuggestionsService::UploadOneFromBlacklist, | |
| 360 weak_ptr_factory_.GetWeakPtr()); | |
| 361 BrowserThread::PostDelayedTask( | |
| 362 BrowserThread::UI, FROM_HERE, blacklist_cb, | |
| 363 base::TimeDelta::FromSeconds(blacklist_delay_sec_)); | |
| 364 } | |
| 365 } | |
| 366 | |
| 367 void SuggestionsService::UploadOneFromBlacklist() { | |
| 368 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 369 | |
| 370 // If there's an ongoing request, let it complete. | |
| 371 if (pending_request_.get()) return; | |
| 372 | |
| 373 GURL blacklist_url; | |
| 374 if (!blacklist_store_->GetFirstUrlFromBlacklist(&blacklist_url)) | |
| 375 return; // Local blacklist is empty. | |
| 376 | |
| 377 // Send blacklisting request. | |
| 378 IssueRequest(BuildBlacklistRequestURL(blacklist_url_prefix_, blacklist_url)); | |
| 379 } | |
| 380 | |
| 381 void SuggestionsService::UpdateBlacklistDelay(bool last_request_successful) { | |
| 382 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 383 | |
| 384 if (last_request_successful) { | |
| 385 blacklist_delay_sec_ = kBlacklistDefaultDelaySec; | |
| 386 } else { | |
| 387 unsigned int candidate_delay = | |
| 388 blacklist_delay_sec_ * kBlacklistBackoffMultiplier; | |
| 389 if (candidate_delay < kBlacklistMaxDelaySec) | |
| 390 blacklist_delay_sec_ = candidate_delay; | |
| 391 } | |
| 289 } | 392 } |
| 290 | 393 |
| 291 } // namespace suggestions | 394 } // namespace suggestions |
| OLD | NEW |