• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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