1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/autofill/autofill_download.h"
6
7 #include <algorithm>
8 #include <ostream>
9 #include <vector>
10
11 #ifdef ANDROID
12 #include "android/jni/autofill_request_url.h"
13 #endif
14 #include "base/logging.h"
15 #include "base/rand_util.h"
16 #include "base/stl_util-inl.h"
17 #include "base/string_util.h"
18 #include "chrome/browser/autofill/autofill_metrics.h"
19 #include "chrome/browser/autofill/autofill_xml_parser.h"
20 #include "chrome/browser/autofill/form_structure.h"
21 #include "chrome/browser/prefs/pref_service.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/common/pref_names.h"
24 #include "googleurl/src/gurl.h"
25 #include "net/http/http_response_headers.h"
26 #include "third_party/libjingle/source/talk/xmllite/xmlparser.h"
27
28 #define AUTO_FILL_QUERY_SERVER_REQUEST_URL \
29 "http://toolbarqueries.clients.google.com:80/tbproxy/af/query"
30 #define AUTO_FILL_UPLOAD_SERVER_REQUEST_URL \
31 "http://toolbarqueries.clients.google.com:80/tbproxy/af/upload"
32 #define AUTO_FILL_QUERY_SERVER_NAME_START_IN_HEADER "GFE/"
33
34 namespace {
35 const size_t kMaxFormCacheSize = 16;
36 };
37
38 struct AutofillDownloadManager::FormRequestData {
39 std::vector<std::string> form_signatures;
40 AutofillRequestType request_type;
41 };
42
43 #ifdef ANDROID
44 // Taken from autofill_manager.cc
45 const double kAutoFillPositiveUploadRateDefaultValue = 0.01;
46 const double kAutoFillNegativeUploadRateDefaultValue = 0.01;
47 #endif
48
AutofillDownloadManager(Profile * profile)49 AutofillDownloadManager::AutofillDownloadManager(Profile* profile)
50 : profile_(profile),
51 observer_(NULL),
52 max_form_cache_size_(kMaxFormCacheSize),
53 next_query_request_(base::Time::Now()),
54 next_upload_request_(base::Time::Now()),
55 positive_upload_rate_(0),
56 negative_upload_rate_(0),
57 fetcher_id_for_unittest_(0) {
58 // |profile_| could be NULL in some unit-tests.
59 #ifdef ANDROID
60 positive_upload_rate_ = kAutoFillPositiveUploadRateDefaultValue;
61 negative_upload_rate_ = kAutoFillNegativeUploadRateDefaultValue;
62 #else
63 if (profile_) {
64 PrefService* preferences = profile_->GetPrefs();
65 positive_upload_rate_ =
66 preferences->GetDouble(prefs::kAutofillPositiveUploadRate);
67 negative_upload_rate_ =
68 preferences->GetDouble(prefs::kAutofillNegativeUploadRate);
69 }
70 #endif
71 }
72
~AutofillDownloadManager()73 AutofillDownloadManager::~AutofillDownloadManager() {
74 STLDeleteContainerPairFirstPointers(url_fetchers_.begin(),
75 url_fetchers_.end());
76 }
77
SetObserver(AutofillDownloadManager::Observer * observer)78 void AutofillDownloadManager::SetObserver(
79 AutofillDownloadManager::Observer *observer) {
80 if (observer) {
81 DCHECK(!observer_);
82 observer_ = observer;
83 } else {
84 observer_ = NULL;
85 }
86 }
87
StartQueryRequest(const ScopedVector<FormStructure> & forms,const AutofillMetrics & metric_logger)88 bool AutofillDownloadManager::StartQueryRequest(
89 const ScopedVector<FormStructure>& forms,
90 const AutofillMetrics& metric_logger) {
91 if (next_query_request_ > base::Time::Now()) {
92 // We are in back-off mode: do not do the request.
93 return false;
94 }
95 std::string form_xml;
96 FormRequestData request_data;
97 if (!FormStructure::EncodeQueryRequest(forms, &request_data.form_signatures,
98 &form_xml)) {
99 return false;
100 }
101
102 request_data.request_type = AutofillDownloadManager::REQUEST_QUERY;
103 metric_logger.Log(AutofillMetrics::QUERY_SENT);
104
105 std::string query_data;
106 if (CheckCacheForQueryRequest(request_data.form_signatures, &query_data)) {
107 VLOG(1) << "AutofillDownloadManager: query request has been retrieved from"
108 << "the cache";
109 if (observer_)
110 observer_->OnLoadedAutofillHeuristics(query_data);
111 return true;
112 }
113
114 return StartRequest(form_xml, request_data);
115 }
116
StartUploadRequest(const FormStructure & form,bool form_was_matched)117 bool AutofillDownloadManager::StartUploadRequest(
118 const FormStructure& form, bool form_was_matched) {
119 if (next_upload_request_ > base::Time::Now()) {
120 // We are in back-off mode: do not do the request.
121 return false;
122 }
123
124 // Check if we need to upload form.
125 double upload_rate = form_was_matched ? GetPositiveUploadRate() :
126 GetNegativeUploadRate();
127 if (base::RandDouble() > upload_rate) {
128 VLOG(1) << "AutofillDownloadManager: Upload request is ignored";
129 // If we ever need notification that upload was skipped, add it here.
130 return false;
131 }
132 std::string form_xml;
133 if (!form.EncodeUploadRequest(form_was_matched, &form_xml))
134 return false;
135
136 FormRequestData request_data;
137 request_data.form_signatures.push_back(form.FormSignature());
138 request_data.request_type = AutofillDownloadManager::REQUEST_UPLOAD;
139
140 return StartRequest(form_xml, request_data);
141 }
142
CancelRequest(const std::string & form_signature,AutofillDownloadManager::AutofillRequestType request_type)143 bool AutofillDownloadManager::CancelRequest(
144 const std::string& form_signature,
145 AutofillDownloadManager::AutofillRequestType request_type) {
146 for (std::map<URLFetcher *, FormRequestData>::iterator it =
147 url_fetchers_.begin();
148 it != url_fetchers_.end();
149 ++it) {
150 if (std::find(it->second.form_signatures.begin(),
151 it->second.form_signatures.end(), form_signature) !=
152 it->second.form_signatures.end() &&
153 it->second.request_type == request_type) {
154 delete it->first;
155 url_fetchers_.erase(it);
156 return true;
157 }
158 }
159 return false;
160 }
161
GetPositiveUploadRate() const162 double AutofillDownloadManager::GetPositiveUploadRate() const {
163 return positive_upload_rate_;
164 }
165
GetNegativeUploadRate() const166 double AutofillDownloadManager::GetNegativeUploadRate() const {
167 return negative_upload_rate_;
168 }
169
SetPositiveUploadRate(double rate)170 void AutofillDownloadManager::SetPositiveUploadRate(double rate) {
171 if (rate == positive_upload_rate_)
172 return;
173 positive_upload_rate_ = rate;
174 DCHECK_GE(rate, 0.0);
175 DCHECK_LE(rate, 1.0);
176 DCHECK(profile_);
177 #ifndef ANDROID
178 PrefService* preferences = profile_->GetPrefs();
179 preferences->SetDouble(prefs::kAutofillPositiveUploadRate, rate);
180 #endif
181 }
182
SetNegativeUploadRate(double rate)183 void AutofillDownloadManager::SetNegativeUploadRate(double rate) {
184 if (rate == negative_upload_rate_)
185 return;
186 negative_upload_rate_ = rate;
187 DCHECK_GE(rate, 0.0);
188 DCHECK_LE(rate, 1.0);
189 DCHECK(profile_);
190 #ifndef ANDROID
191 PrefService* preferences = profile_->GetPrefs();
192 preferences->SetDouble(prefs::kAutofillNegativeUploadRate, rate);
193 #endif
194 }
195
StartRequest(const std::string & form_xml,const FormRequestData & request_data)196 bool AutofillDownloadManager::StartRequest(
197 const std::string& form_xml,
198 const FormRequestData& request_data) {
199 net::URLRequestContextGetter* request_context =
200 #ifdef ANDROID
201 // On Android, use the webview request context getter which was passed
202 // through in the WebAutoFill::init() method in WebKit.
203 profile_->GetRequestContext();
204 #else
205 Profile::GetDefaultRequestContext();
206 #endif
207 // Check if default request context is NULL: this very rarely happens,
208 // I think, this could happen only if user opens chrome with some pages
209 // loading the forms immediately; I cannot reproduce this even in that
210 // scenario, but bug 74492 shows it happened at least once. In that case bail
211 // out and fall back on our own heuristics.
212 if (!request_context)
213 return false;
214 std::string request_url;
215 if (request_data.request_type == AutofillDownloadManager::REQUEST_QUERY)
216 request_url = AUTO_FILL_QUERY_SERVER_REQUEST_URL;
217 else
218 request_url = AUTO_FILL_UPLOAD_SERVER_REQUEST_URL;
219
220 #ifdef ANDROID
221 if (request_data.request_type == AutofillDownloadManager::REQUEST_QUERY) {
222 // Ask the platform what URL to use for Autofill. If the
223 // platform doesn't know, bail and rely on the heuristics
224 // we've gathered.
225 request_url = android::AutofillRequestUrl::GetQueryUrl();
226 if (request_url.empty())
227 return false;
228 }
229 #endif
230
231 // Id is ignored for regular chrome, in unit test id's for fake fetcher
232 // factory will be 0, 1, 2, ...
233 URLFetcher *fetcher = URLFetcher::Create(fetcher_id_for_unittest_++,
234 GURL(request_url),
235 URLFetcher::POST,
236 this);
237 url_fetchers_[fetcher] = request_data;
238 fetcher->set_automatically_retry_on_5xx(false);
239 fetcher->set_request_context(request_context);
240 fetcher->set_upload_data("text/plain", form_xml);
241 fetcher->Start();
242 return true;
243 }
244
CacheQueryRequest(const std::vector<std::string> & forms_in_query,const std::string & query_data)245 void AutofillDownloadManager::CacheQueryRequest(
246 const std::vector<std::string>& forms_in_query,
247 const std::string& query_data) {
248 std::string signature = GetCombinedSignature(forms_in_query);
249 for (QueryRequestCache::iterator it = cached_forms_.begin();
250 it != cached_forms_.end(); ++it) {
251 if (it->first == signature) {
252 // We hit the cache, move to the first position and return.
253 std::pair<std::string, std::string> data = *it;
254 cached_forms_.erase(it);
255 cached_forms_.push_front(data);
256 return;
257 }
258 }
259 std::pair<std::string, std::string> data;
260 data.first = signature;
261 data.second = query_data;
262 cached_forms_.push_front(data);
263 while (cached_forms_.size() > max_form_cache_size_)
264 cached_forms_.pop_back();
265 }
266
CheckCacheForQueryRequest(const std::vector<std::string> & forms_in_query,std::string * query_data) const267 bool AutofillDownloadManager::CheckCacheForQueryRequest(
268 const std::vector<std::string>& forms_in_query,
269 std::string* query_data) const {
270 std::string signature = GetCombinedSignature(forms_in_query);
271 for (QueryRequestCache::const_iterator it = cached_forms_.begin();
272 it != cached_forms_.end(); ++it) {
273 if (it->first == signature) {
274 // We hit the cache, fill the data and return.
275 *query_data = it->second;
276 return true;
277 }
278 }
279 return false;
280 }
281
GetCombinedSignature(const std::vector<std::string> & forms_in_query) const282 std::string AutofillDownloadManager::GetCombinedSignature(
283 const std::vector<std::string>& forms_in_query) const {
284 size_t total_size = forms_in_query.size();
285 for (size_t i = 0; i < forms_in_query.size(); ++i)
286 total_size += forms_in_query[i].length();
287 std::string signature;
288
289 signature.reserve(total_size);
290
291 for (size_t i = 0; i < forms_in_query.size(); ++i) {
292 if (i)
293 signature.append(",");
294 signature.append(forms_in_query[i]);
295 }
296 return signature;
297 }
298
OnURLFetchComplete(const URLFetcher * source,const GURL & url,const net::URLRequestStatus & status,int response_code,const ResponseCookies & cookies,const std::string & data)299 void AutofillDownloadManager::OnURLFetchComplete(
300 const URLFetcher* source,
301 const GURL& url,
302 const net::URLRequestStatus& status,
303 int response_code,
304 const ResponseCookies& cookies,
305 const std::string& data) {
306 std::map<URLFetcher *, FormRequestData>::iterator it =
307 url_fetchers_.find(const_cast<URLFetcher*>(source));
308 if (it == url_fetchers_.end()) {
309 // Looks like crash on Mac is possibly caused with callback entering here
310 // with unknown fetcher when network is refreshed.
311 return;
312 }
313 std::string type_of_request(
314 it->second.request_type == AutofillDownloadManager::REQUEST_QUERY ?
315 "query" : "upload");
316 const int kHttpResponseOk = 200;
317 const int kHttpInternalServerError = 500;
318 const int kHttpBadGateway = 502;
319 const int kHttpServiceUnavailable = 503;
320
321 CHECK(it->second.form_signatures.size());
322 if (response_code != kHttpResponseOk) {
323 bool back_off = false;
324 std::string server_header;
325 switch (response_code) {
326 case kHttpBadGateway:
327 if (!source->response_headers()->EnumerateHeader(NULL, "server",
328 &server_header) ||
329 StartsWithASCII(server_header.c_str(),
330 AUTO_FILL_QUERY_SERVER_NAME_START_IN_HEADER,
331 false) != 0)
332 break;
333 // Bad gateway was received from Autofill servers. Fall through to back
334 // off.
335 case kHttpInternalServerError:
336 case kHttpServiceUnavailable:
337 back_off = true;
338 break;
339 }
340
341 if (back_off) {
342 base::Time back_off_time(base::Time::Now() + source->backoff_delay());
343 if (it->second.request_type == AutofillDownloadManager::REQUEST_QUERY) {
344 next_query_request_ = back_off_time;
345 } else {
346 next_upload_request_ = back_off_time;
347 }
348 }
349
350 LOG(WARNING) << "AutofillDownloadManager: " << type_of_request
351 << " request has failed with response " << response_code;
352 if (observer_) {
353 observer_->OnHeuristicsRequestError(it->second.form_signatures[0],
354 it->second.request_type,
355 response_code);
356 }
357 } else {
358 VLOG(1) << "AutofillDownloadManager: " << type_of_request
359 << " request has succeeded";
360 if (it->second.request_type == AutofillDownloadManager::REQUEST_QUERY) {
361 CacheQueryRequest(it->second.form_signatures, data);
362 if (observer_)
363 observer_->OnLoadedAutofillHeuristics(data);
364 } else {
365 double new_positive_upload_rate = 0;
366 double new_negative_upload_rate = 0;
367 AutofillUploadXmlParser parse_handler(&new_positive_upload_rate,
368 &new_negative_upload_rate);
369 buzz::XmlParser parser(&parse_handler);
370 parser.Parse(data.data(), data.length(), true);
371 if (parse_handler.succeeded()) {
372 SetPositiveUploadRate(new_positive_upload_rate);
373 SetNegativeUploadRate(new_negative_upload_rate);
374 }
375
376 if (observer_)
377 observer_->OnUploadedAutofillHeuristics(it->second.form_signatures[0]);
378 }
379 }
380 delete it->first;
381 url_fetchers_.erase(it);
382 }
383