Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
|
battre
2017/04/07 08:42:27
I would suggest to reduce this file significantly
Ramin Halavati
2017/04/07 11:33:32
I am not sure if this is the correct way to add te
battre
2017/04/10 10:58:31
SGTM. Keep in mind that the current style of testi
| |
| 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 "components/autofill/core/browser/autofill_download_manager.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/location.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/numerics/safe_conversions.h" | |
| 13 #include "base/rand_util.h" | |
| 14 #include "base/strings/string_util.h" | |
| 15 #include "base/threading/thread_task_runner_handle.h" | |
| 16 #include "components/autofill/core/browser/autofill_driver.h" | |
| 17 #include "components/autofill/core/browser/autofill_metrics.h" | |
| 18 #include "components/autofill/core/browser/form_structure.h" | |
| 19 #include "components/autofill/core/browser/proto/server.pb.h" | |
| 20 #include "components/autofill/core/common/autofill_pref_names.h" | |
| 21 #include "components/data_use_measurement/core/data_use_user_data.h" | |
| 22 #include "components/variations/net/variations_http_headers.h" | |
| 23 #include "net/base/load_flags.h" | |
| 24 #include "net/http/http_request_headers.h" | |
| 25 #include "net/http/http_response_headers.h" | |
| 26 #include "net/http/http_status_code.h" | |
| 27 #include "net/traffic_annotation/network_traffic_annotation.h" | |
| 28 #include "net/url_request/url_fetcher.h" | |
| 29 #include "url/gurl.h" | |
| 30 | |
| 31 namespace { | |
| 32 | |
| 33 net::NetworkTrafficAnnotationTag GetNetworkTrafficAnnotation( | |
| 34 const autofill::AutofillDownloadManager::RequestType& request_type) { | |
| 35 if (request_type == autofill::AutofillDownloadManager::REQUEST_QUERY) { | |
| 36 return net::DefineNetworkTrafficAnnotation("autofill_query", R"( | |
| 37 semantics { | |
| 38 sender: "Autofill" | |
| 39 description: | |
| 40 "Chromium can automatically fill in web forms. If the feature is " | |
| 41 "enabled, Chromium will send a non-identifying description of the " | |
| 42 "form to Google's servers, which will respond with the type of " | |
| 43 "data required by each of the form's fields, if known. I.e., if a " | |
| 44 "field expects to receive a name, phone number, street address, " | |
| 45 "etc." | |
| 46 trigger: "User encounters a web form." | |
| 47 data: | |
| 48 "Hashed descriptions of the form and its fields. User data is not " | |
| 49 "sent." | |
| 50 destination: GOOGLE_OWNED_SERVICE | |
| 51 } | |
| 52 policy { | |
| 53 cookies_allowed: false | |
| 54 setting: | |
| 55 "You can enable or disable this feature via 'Enable autofill to " | |
| 56 "fill out web forms in a single click.' in Chromium's settings " | |
| 57 "under 'Passwords and forms'. The feature is enabled by default." | |
| 58 chrome_policy { | |
| 59 AutofillEnabled { | |
| 60 policy_options {mode: MANDATORY} | |
| 61 AutofillEnabled: false | |
| 62 } | |
| 63 } | |
| 64 })"); | |
| 65 } | |
| 66 | |
| 67 DCHECK_EQ(request_type, autofill::AutofillDownloadManager::REQUEST_UPLOAD); | |
| 68 return net::DefineNetworkTrafficAnnotation("autofill_upload", R"( | |
| 69 semantics { | |
| 70 sender: "Autofill" | |
| 71 description: | |
| 72 "Chromium relies on crowd-sourced field type classifications to " | |
| 73 "help it automatically fill in web forms. If the feature is " | |
| 74 "enabled, Chromium will send a non-identifying description of the " | |
| 75 "form to Google's servers along with the type of data Chromium " | |
| 76 "observed being given to the form. I.e., if you entered your first " | |
| 77 "name into a form field, Chromium will 'vote' for that form field " | |
| 78 "being a first name field." | |
| 79 trigger: "User submits a web form." | |
| 80 data: | |
| 81 "Hashed descriptions of the form and its fields along with type of " | |
| 82 "data given to each field, if recognized from the user's " | |
| 83 "profile(s). User data is not sent." | |
| 84 destination: GOOGLE_OWNED_SERVICE | |
| 85 } | |
| 86 policy { | |
| 87 cookies_allowed: false | |
| 88 setting: | |
| 89 "You can enable or disable this feature via 'Enable autofill to " | |
| 90 "fill out web forms in a single click.' in Chromium's settings " | |
| 91 "under 'Passwords and forms'. The feature is enabled by default." | |
| 92 chrome_policy { | |
| 93 AutofillEnabled { | |
| 94 policy_options {mode: MANDATORY} | |
| 95 AutofillEnabled: false | |
| 96 } | |
| 97 } | |
| 98 })"); | |
| 99 } | |
| 100 | |
| 101 } // namespace | |
| 102 | |
| 103 namespace autofill { | |
| 104 | |
| 105 namespace { | |
| 106 | |
| 107 const size_t kMaxFormCacheSize = 16; | |
| 108 const size_t kMaxFieldsPerQueryRequest = 100; | |
| 109 | |
| 110 const net::BackoffEntry::Policy kAutofillBackoffPolicy = { | |
| 111 // Number of initial errors (in sequence) to ignore before applying | |
| 112 // exponential back-off rules. | |
| 113 0, | |
| 114 | |
| 115 // Initial delay for exponential back-off in ms. | |
| 116 1000, // 1 second. | |
| 117 | |
| 118 // Factor by which the waiting time will be multiplied. | |
| 119 2, | |
| 120 | |
| 121 // Fuzzing percentage. ex: 10% will spread requests randomly | |
| 122 // between 90%-100% of the calculated time. | |
| 123 0.33, // 33%. | |
| 124 | |
| 125 // Maximum amount of time we are willing to delay our request in ms. | |
| 126 30 * 1000, // 30 seconds. | |
| 127 | |
| 128 // Time to keep an entry from being discarded even when it | |
| 129 // has no significant state, -1 to never discard. | |
| 130 -1, | |
| 131 | |
| 132 // Don't use initial delay unless the last request was an error. | |
| 133 false, | |
| 134 }; | |
| 135 | |
| 136 #if defined(GOOGLE_CHROME_BUILD) | |
| 137 const char kClientName[] = "Google Chrome"; | |
| 138 #else | |
| 139 const char kClientName[] = "Chromium"; | |
| 140 #endif // defined(GOOGLE_CHROME_BUILD) | |
| 141 | |
| 142 size_t CountActiveFieldsInForms(const std::vector<FormStructure*>& forms) { | |
| 143 size_t active_field_count = 0; | |
| 144 for (const auto* form : forms) | |
| 145 active_field_count += form->active_field_count(); | |
| 146 return active_field_count; | |
| 147 } | |
| 148 | |
| 149 std::string RequestTypeToString(AutofillDownloadManager::RequestType type) { | |
| 150 switch (type) { | |
| 151 case AutofillDownloadManager::REQUEST_QUERY: | |
| 152 return "query"; | |
| 153 case AutofillDownloadManager::REQUEST_UPLOAD: | |
| 154 return "upload"; | |
| 155 } | |
| 156 NOTREACHED(); | |
| 157 return std::string(); | |
| 158 } | |
| 159 | |
| 160 GURL GetRequestUrl(AutofillDownloadManager::RequestType request_type) { | |
| 161 return GURL("https://clients1.google.com/tbproxy/af/" + | |
| 162 RequestTypeToString(request_type) + "?client=" + kClientName); | |
| 163 } | |
| 164 | |
| 165 std::ostream& operator<<(std::ostream& out, | |
| 166 const autofill::AutofillQueryContents& query) { | |
| 167 out << "client_version: " << query.client_version(); | |
| 168 for (const auto& form : query.form()) { | |
| 169 out << "\nForm\n signature: " << form.signature(); | |
| 170 for (const auto& field : form.field()) { | |
| 171 out << "\n Field\n signature: " << field.signature(); | |
| 172 if (!field.name().empty()) | |
| 173 out << "\n name: " << field.name(); | |
| 174 if (!field.type().empty()) | |
| 175 out << "\n type: " << field.type(); | |
| 176 } | |
| 177 } | |
| 178 return out; | |
| 179 } | |
| 180 | |
| 181 std::ostream& operator<<(std::ostream& out, | |
| 182 const autofill::AutofillUploadContents& upload) { | |
| 183 out << "client_version: " << upload.client_version() << "\n"; | |
| 184 out << "form_signature: " << upload.form_signature() << "\n"; | |
| 185 out << "data_present: " << upload.data_present() << "\n"; | |
| 186 out << "submission: " << upload.submission() << "\n"; | |
| 187 if (!upload.action_signature()) | |
| 188 out << "action_signature: " << upload.action_signature() << "\n"; | |
| 189 if (!upload.login_form_signature()) | |
| 190 out << "login_form_signature: " << upload.login_form_signature() << "\n"; | |
| 191 if (!upload.form_name().empty()) | |
| 192 out << "form_name: " << upload.form_name() << "\n"; | |
| 193 | |
| 194 for (const auto& field : upload.field()) { | |
| 195 out << "\n Field" | |
| 196 << "\n signature: " << field.signature() | |
| 197 << "\n autofill_type: " << field.autofill_type(); | |
| 198 if (!field.name().empty()) | |
| 199 out << "\n name: " << field.name(); | |
| 200 if (!field.autocomplete().empty()) | |
| 201 out << "\n autocomplete: " << field.autocomplete(); | |
| 202 if (!field.type().empty()) | |
| 203 out << "\n type: " << field.type(); | |
| 204 if (field.generation_type()) | |
| 205 out << "\n generation_type: " << field.generation_type(); | |
| 206 } | |
| 207 return out; | |
| 208 } | |
| 209 | |
| 210 } // namespace | |
| 211 | |
| 212 struct AutofillDownloadManager::FormRequestData { | |
| 213 std::vector<std::string> form_signatures; | |
| 214 RequestType request_type; | |
| 215 std::string payload; | |
| 216 }; | |
| 217 | |
| 218 AutofillDownloadManager::AutofillDownloadManager(AutofillDriver* driver, | |
| 219 Observer* observer) | |
| 220 : driver_(driver), | |
| 221 observer_(observer), | |
| 222 max_form_cache_size_(kMaxFormCacheSize), | |
| 223 fetcher_backoff_(&kAutofillBackoffPolicy), | |
| 224 fetcher_id_for_unittest_(0), | |
| 225 weak_factory_(this) { | |
| 226 DCHECK(observer_); | |
| 227 } | |
| 228 | |
| 229 AutofillDownloadManager::~AutofillDownloadManager() = default; | |
| 230 | |
| 231 bool AutofillDownloadManager::StartQueryRequest( | |
| 232 const std::vector<FormStructure*>& forms) { | |
| 233 // Do not send the request if it contains more fields than the server can | |
| 234 // accept. | |
| 235 if (CountActiveFieldsInForms(forms) > kMaxFieldsPerQueryRequest) | |
| 236 return false; | |
| 237 | |
| 238 AutofillQueryContents query; | |
| 239 FormRequestData request_data; | |
| 240 if (!FormStructure::EncodeQueryRequest(forms, &request_data.form_signatures, | |
| 241 &query)) { | |
| 242 return false; | |
| 243 } | |
| 244 | |
| 245 std::string payload; | |
| 246 if (!query.SerializeToString(&payload)) | |
| 247 return false; | |
| 248 | |
| 249 request_data.request_type = AutofillDownloadManager::REQUEST_QUERY; | |
| 250 request_data.payload = payload; | |
| 251 AutofillMetrics::LogServerQueryMetric(AutofillMetrics::QUERY_SENT); | |
| 252 | |
| 253 std::string query_data; | |
| 254 if (CheckCacheForQueryRequest(request_data.form_signatures, &query_data)) { | |
| 255 VLOG(1) << "AutofillDownloadManager: query request has been retrieved " | |
| 256 << "from the cache, form signatures: " | |
| 257 << GetCombinedSignature(request_data.form_signatures); | |
| 258 observer_->OnLoadedServerPredictions(std::move(query_data), | |
| 259 request_data.form_signatures); | |
| 260 return true; | |
| 261 } | |
| 262 | |
| 263 VLOG(1) << "Sending Autofill Query Request:\n" << query; | |
| 264 | |
| 265 return StartRequest(request_data); | |
| 266 } | |
| 267 | |
| 268 bool AutofillDownloadManager::StartUploadRequest( | |
| 269 const FormStructure& form, | |
| 270 bool form_was_autofilled, | |
| 271 const ServerFieldTypeSet& available_field_types, | |
| 272 const std::string& login_form_signature, | |
| 273 bool observed_submission) { | |
| 274 AutofillUploadContents upload; | |
| 275 if (!form.EncodeUploadRequest(available_field_types, form_was_autofilled, | |
| 276 login_form_signature, observed_submission, | |
| 277 &upload)) | |
| 278 return false; | |
| 279 | |
| 280 std::string payload; | |
| 281 if (!upload.SerializeToString(&payload)) | |
| 282 return false; | |
| 283 | |
| 284 if (form.upload_required() == UPLOAD_NOT_REQUIRED) { | |
| 285 VLOG(1) << "AutofillDownloadManager: Upload request is ignored."; | |
| 286 // If we ever need notification that upload was skipped, add it here. | |
| 287 return false; | |
| 288 } | |
| 289 | |
| 290 FormRequestData request_data; | |
| 291 request_data.form_signatures.push_back(form.FormSignatureAsStr()); | |
| 292 request_data.request_type = AutofillDownloadManager::REQUEST_UPLOAD; | |
| 293 request_data.payload = payload; | |
| 294 | |
| 295 VLOG(1) << "Sending Autofill Upload Request:\n" << upload; | |
| 296 | |
| 297 return StartRequest(request_data); | |
| 298 } | |
| 299 | |
| 300 bool AutofillDownloadManager::StartRequest( | |
| 301 const FormRequestData& request_data) { | |
| 302 net::URLRequestContextGetter* request_context = | |
| 303 driver_->GetURLRequestContext(); | |
| 304 DCHECK(request_context); | |
| 305 GURL request_url = GetRequestUrl(request_data.request_type); | |
| 306 | |
| 307 // Id is ignored for regular chrome, in unit test id's for fake fetcher | |
| 308 // factory will be 0, 1, 2, ... | |
| 309 std::unique_ptr<net::URLFetcher> owned_fetcher = net::URLFetcher::Create( | |
| 310 fetcher_id_for_unittest_++, request_url, net::URLFetcher::POST, this, | |
| 311 GetNetworkTrafficAnnotation(request_data.request_type)); | |
| 312 net::URLFetcher* fetcher = owned_fetcher.get(); | |
| 313 data_use_measurement::DataUseUserData::AttachToFetcher( | |
| 314 fetcher, data_use_measurement::DataUseUserData::AUTOFILL); | |
| 315 url_fetchers_[fetcher] = | |
| 316 std::make_pair(std::move(owned_fetcher), request_data); | |
| 317 fetcher->SetAutomaticallyRetryOn5xx(false); | |
| 318 fetcher->SetRequestContext(request_context); | |
| 319 fetcher->SetUploadData("text/proto", request_data.payload); | |
| 320 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES | | |
| 321 net::LOAD_DO_NOT_SEND_COOKIES); | |
| 322 // Add Chrome experiment state to the request headers. | |
| 323 net::HttpRequestHeaders headers; | |
| 324 // Note: It's OK to pass |is_signed_in| false if it's unknown, as it does | |
| 325 // not affect transmission of experiments coming from the variations server. | |
| 326 bool is_signed_in = false; | |
| 327 variations::AppendVariationHeaders(fetcher->GetOriginalURL(), | |
| 328 driver_->IsOffTheRecord(), false, | |
| 329 is_signed_in, &headers); | |
| 330 fetcher->SetExtraRequestHeaders(headers.ToString()); | |
| 331 fetcher->Start(); | |
| 332 | |
| 333 return true; | |
| 334 } | |
| 335 | |
| 336 void AutofillDownloadManager::CacheQueryRequest( | |
| 337 const std::vector<std::string>& forms_in_query, | |
| 338 const std::string& query_data) { | |
| 339 std::string signature = GetCombinedSignature(forms_in_query); | |
| 340 for (auto it = cached_forms_.begin(); it != cached_forms_.end(); ++it) { | |
| 341 if (it->first == signature) { | |
| 342 // We hit the cache, move to the first position and return. | |
| 343 std::pair<std::string, std::string> data = *it; | |
| 344 cached_forms_.erase(it); | |
| 345 cached_forms_.push_front(data); | |
| 346 return; | |
| 347 } | |
| 348 } | |
| 349 std::pair<std::string, std::string> data; | |
| 350 data.first = signature; | |
| 351 data.second = query_data; | |
| 352 cached_forms_.push_front(data); | |
| 353 while (cached_forms_.size() > max_form_cache_size_) | |
| 354 cached_forms_.pop_back(); | |
| 355 } | |
| 356 | |
| 357 bool AutofillDownloadManager::CheckCacheForQueryRequest( | |
| 358 const std::vector<std::string>& forms_in_query, | |
| 359 std::string* query_data) const { | |
| 360 std::string signature = GetCombinedSignature(forms_in_query); | |
| 361 for (const auto& it : cached_forms_) { | |
| 362 if (it.first == signature) { | |
| 363 // We hit the cache, fill the data and return. | |
| 364 *query_data = it.second; | |
| 365 return true; | |
| 366 } | |
| 367 } | |
| 368 return false; | |
| 369 } | |
| 370 | |
| 371 std::string AutofillDownloadManager::GetCombinedSignature( | |
| 372 const std::vector<std::string>& forms_in_query) const { | |
| 373 size_t total_size = forms_in_query.size(); | |
| 374 for (size_t i = 0; i < forms_in_query.size(); ++i) | |
| 375 total_size += forms_in_query[i].length(); | |
| 376 std::string signature; | |
| 377 | |
| 378 signature.reserve(total_size); | |
| 379 | |
| 380 for (size_t i = 0; i < forms_in_query.size(); ++i) { | |
| 381 if (i) | |
| 382 signature.append(","); | |
| 383 signature.append(forms_in_query[i]); | |
| 384 } | |
| 385 return signature; | |
| 386 } | |
| 387 | |
| 388 void AutofillDownloadManager::OnURLFetchComplete( | |
| 389 const net::URLFetcher* source) { | |
| 390 auto it = url_fetchers_.find(const_cast<net::URLFetcher*>(source)); | |
| 391 if (it == url_fetchers_.end()) { | |
| 392 // Looks like crash on Mac is possibly caused with callback entering here | |
| 393 // with unknown fetcher when network is refreshed. | |
| 394 return; | |
| 395 } | |
| 396 std::string request_type(RequestTypeToString(it->second.second.request_type)); | |
| 397 | |
| 398 CHECK(it->second.second.form_signatures.size()); | |
| 399 bool success = source->GetResponseCode() == net::HTTP_OK; | |
| 400 fetcher_backoff_.InformOfRequest(success); | |
| 401 | |
| 402 if (!success) { | |
| 403 // Reschedule with the appropriate delay, ignoring return value because | |
| 404 // payload is already well formed. | |
| 405 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
| 406 FROM_HERE, | |
| 407 base::Bind(base::IgnoreResult(&AutofillDownloadManager::StartRequest), | |
| 408 weak_factory_.GetWeakPtr(), it->second.second), | |
| 409 fetcher_backoff_.GetTimeUntilRelease()); | |
| 410 | |
| 411 VLOG(1) << "AutofillDownloadManager: " << request_type | |
| 412 << " request has failed with response " | |
| 413 << source->GetResponseCode(); | |
| 414 observer_->OnServerRequestError(it->second.second.form_signatures[0], | |
| 415 it->second.second.request_type, | |
| 416 source->GetResponseCode()); | |
| 417 } else { | |
| 418 std::string response_body; | |
| 419 source->GetResponseAsString(&response_body); | |
| 420 if (it->second.second.request_type == | |
| 421 AutofillDownloadManager::REQUEST_QUERY) { | |
| 422 CacheQueryRequest(it->second.second.form_signatures, response_body); | |
| 423 observer_->OnLoadedServerPredictions(std::move(response_body), | |
| 424 it->second.second.form_signatures); | |
| 425 } else { | |
| 426 VLOG(1) << "AutofillDownloadManager: upload request has succeeded."; | |
| 427 observer_->OnUploadedPossibleFieldTypes(); | |
| 428 } | |
| 429 } | |
| 430 url_fetchers_.erase(it); | |
| 431 } | |
| 432 | |
| 433 } // namespace autofill | |
| OLD | NEW |