| 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 |