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