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