Home | History | Annotate | Download | only in autofill
      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 
     37 namespace {
     38 const size_t kMaxFormCacheSize = 16;
     39 };
     40 
     41 struct AutofillDownloadManager::FormRequestData {
     42   std::vector<std::string> form_signatures;
     43   AutofillRequestType request_type;
     44 };
     45 
     46 #ifdef ANDROID
     47 // Taken from autofill_manager.cc
     48 const double kAutoFillPositiveUploadRateDefaultValue = 0.01;
     49 const double kAutoFillNegativeUploadRateDefaultValue = 0.01;
     50 #endif
     51 
     52 AutofillDownloadManager::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 
     76 AutofillDownloadManager::~AutofillDownloadManager() {
     77   STLDeleteContainerPairFirstPointers(url_fetchers_.begin(),
     78                                       url_fetchers_.end());
     79 }
     80 
     81 void AutofillDownloadManager::SetObserver(
     82     AutofillDownloadManager::Observer *observer) {
     83   if (observer) {
     84     DCHECK(!observer_);
     85     observer_ = observer;
     86   } else {
     87     observer_ = NULL;
     88   }
     89 }
     90 
     91 bool 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 
    120 bool 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 
    146 bool 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 
    165 double AutofillDownloadManager::GetPositiveUploadRate() const {
    166   return positive_upload_rate_;
    167 }
    168 
    169 double AutofillDownloadManager::GetNegativeUploadRate() const {
    170   return negative_upload_rate_;
    171 }
    172 
    173 void 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 
    186 void 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 
    199 bool 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 
    250 void 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 
    272 bool 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 
    287 std::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 
    304 void 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