diff options
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/autofill/autofill_download.cc | 122 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_download.h | 83 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_download_unittest.cc | 240 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager.cc | 85 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager.h | 11 | ||||
-rw-r--r-- | chrome/browser/autofill/form_structure.cc | 128 | ||||
-rw-r--r-- | chrome/browser/autofill/form_structure.h | 29 |
7 files changed, 543 insertions, 155 deletions
diff --git a/chrome/browser/autofill/autofill_download.cc b/chrome/browser/autofill/autofill_download.cc index 0d650d1..9384474 100644 --- a/chrome/browser/autofill/autofill_download.cc +++ b/chrome/browser/autofill/autofill_download.cc @@ -4,6 +4,8 @@ #include "chrome/browser/autofill/autofill_download.h" +#include <algorithm> + #include "base/logging.h" #include "base/rand_util.h" #include "base/stl_util-inl.h" @@ -24,7 +26,10 @@ const double kAutoFillNegativeUploadRate = 0.01; } // namespace AutoFillDownloadManager::AutoFillDownloadManager() - : observer_(NULL) { + : observer_(NULL), + positive_upload_rate_(kAutoFillPositiveUploadRate), + negative_upload_rate_(kAutoFillNegativeUploadRate), + fetcher_id_for_unittest_(0) { } AutoFillDownloadManager::~AutoFillDownloadManager() { @@ -42,48 +47,57 @@ void AutoFillDownloadManager::SetObserver( } } -bool AutoFillDownloadManager::StartRequest(const std::string& form_xml, - const std::string& form_signature, - bool query_data, - bool form_was_matched) { - if (!query_data) { - // Check if we need to upload form. - double upload_rate = form_was_matched ? kAutoFillPositiveUploadRate : - kAutoFillNegativeUploadRate; - if (base::RandDouble() > upload_rate) { - LOG(INFO) << "AutoFillDownloadManager: Upload request is ignored"; - if (observer_) - observer_->OnUploadedAutoFillHeuristics(form_signature); - return true; - } +bool AutoFillDownloadManager::StartQueryRequest( + const ScopedVector<FormStructure>& forms) { + std::string form_xml; + FormStructure::EncodeQueryRequest(forms, &form_xml); + + FormRequestData request_data; + request_data.form_signatures.reserve(forms.size()); + for (ScopedVector<FormStructure>::const_iterator it = forms.begin(); + it != forms.end(); + ++it) { + request_data.form_signatures.push_back((*it)->FormSignature()); } - std::string request_url; - if (query_data) - request_url = AUTO_FILL_QUERY_SERVER_REQUEST_URL; - else - request_url = AUTO_FILL_UPLOAD_SERVER_REQUEST_URL; + request_data.request_type = AutoFillDownloadManager::REQUEST_QUERY; - URLFetcher *fetcher = - URLFetcher::Create(0, GURL(request_url), URLFetcher::POST, this); - FormRequestData data; - data.form_signature = form_signature; - data.query = query_data; - url_fetchers_[fetcher] = data; - fetcher->set_request_context(Profile::GetDefaultRequestContext()); - fetcher->set_upload_data("text/plain", form_xml); - fetcher->Start(); - return true; + return StartRequest(form_xml, request_data); } -bool AutoFillDownloadManager::CancelRequest(const std::string& form_signature, - bool query_data) { +bool AutoFillDownloadManager::StartUploadRequest( + const FormStructure& form, bool form_was_matched) { + // Check if we need to upload form. + // TODO(georgey): adjust this values from returned XML. + double upload_rate = form_was_matched ? positive_upload_rate_ : + negative_upload_rate_; + if (base::RandDouble() > upload_rate) { + LOG(INFO) << "AutoFillDownloadManager: Upload request is ignored"; + if (observer_) + observer_->OnUploadedAutoFillHeuristics(form.FormSignature()); + return true; + } + std::string form_xml; + form.EncodeUploadRequest(form_was_matched, &form_xml); + + FormRequestData request_data; + request_data.form_signatures.push_back(form.FormSignature()); + request_data.request_type = AutoFillDownloadManager::REQUEST_UPLOAD; + + return StartRequest(form_xml, request_data); +} + +bool AutoFillDownloadManager::CancelRequest( + const std::string& form_signature, + AutoFillDownloadManager::AutoFillRequestType request_type) { for (std::map<URLFetcher *, FormRequestData>::iterator it = url_fetchers_.begin(); it != url_fetchers_.end(); ++it) { - if (it->second.form_signature == form_signature && - it->second.query == query_data) { + if (std::find(it->second.form_signatures.begin(), + it->second.form_signatures.end(), form_signature) != + it->second.form_signatures.end() && + it->second.request_type == request_type) { delete it->first; url_fetchers_.erase(it); return true; @@ -92,6 +106,29 @@ bool AutoFillDownloadManager::CancelRequest(const std::string& form_signature, return false; } + +bool AutoFillDownloadManager::StartRequest( + const std::string& form_xml, + const FormRequestData& request_data) { + std::string request_url; + if (request_data.request_type == AutoFillDownloadManager::REQUEST_QUERY) + request_url = AUTO_FILL_QUERY_SERVER_REQUEST_URL; + else + request_url = AUTO_FILL_UPLOAD_SERVER_REQUEST_URL; + + // Id is ignored for regular chrome, in unit test id's for fake fetcher + // factory will be 0, 1, 2, ... + URLFetcher *fetcher = URLFetcher::Create(fetcher_id_for_unittest_++, + GURL(request_url), + URLFetcher::POST, + this); + url_fetchers_[fetcher] = request_data; + fetcher->set_request_context(Profile::GetDefaultRequestContext()); + fetcher->set_upload_data("text/plain", form_xml); + fetcher->Start(); + return true; +} + void AutoFillDownloadManager::OnURLFetchComplete(const URLFetcher* source, const GURL& url, const URLRequestStatus& status, @@ -101,26 +138,33 @@ void AutoFillDownloadManager::OnURLFetchComplete(const URLFetcher* source, std::map<URLFetcher *, FormRequestData>::iterator it = url_fetchers_.find(const_cast<URLFetcher*>(source)); DCHECK(it != url_fetchers_.end()); - std::string type_of_request(it->second.query ? "query" : "upload"); + std::string type_of_request( + it->second.request_type == AutoFillDownloadManager::REQUEST_QUERY ? + "query" : "upload"); const int kHttpResponseOk = 200; + DCHECK(it->second.form_signatures.size()); if (response_code != kHttpResponseOk) { LOG(INFO) << "AutoFillDownloadManager: " << type_of_request << " request has failed with response" << response_code; if (observer_) { - observer_->OnHeuristicsRequestError(it->second.form_signature, + observer_->OnHeuristicsRequestError(it->second.form_signatures[0], + it->second.request_type, response_code); } } else { LOG(INFO) << "AutoFillDownloadManager: " << type_of_request << " request has succeeded"; - if (it->second.query) { + if (it->second.request_type == AutoFillDownloadManager::REQUEST_QUERY) { if (observer_) - observer_->OnLoadedAutoFillHeuristics(it->second.form_signature, data); + observer_->OnLoadedAutoFillHeuristics(it->second.form_signatures, data); } else { + // TODO(georgey): adjust upload probabilities. if (observer_) - observer_->OnUploadedAutoFillHeuristics(it->second.form_signature); + observer_->OnUploadedAutoFillHeuristics(it->second.form_signatures[0]); } } + delete it->first; + url_fetchers_.erase(it); } diff --git a/chrome/browser/autofill/autofill_download.h b/chrome/browser/autofill/autofill_download.h index c9e6ed2..d064db7a 100644 --- a/chrome/browser/autofill/autofill_download.h +++ b/chrome/browser/autofill/autofill_download.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_AUTOFILL_AUTOFILL_DOWNLOAD_H_ #include <map> +#include <vector> #include <string> #include "base/scoped_ptr.h" @@ -19,14 +20,18 @@ // Handles getting and updating AutoFill heuristics. class AutoFillDownloadManager : public URLFetcher::Delegate { public: + enum AutoFillRequestType { + REQUEST_QUERY, + REQUEST_UPLOAD, + }; // An interface used to notify clients of AutoFillDownloadManager. class Observer { public: // Called when heuristic successfully received from server. - // |form_signature| - the signature of the requesting form. + // |form_signatures| - the signatures of the requesting forms. // |heuristic_xml| - server response. virtual void OnLoadedAutoFillHeuristics( - const std::string& form_signature, + const std::vector<std::string>& form_signatures, const std::string& heuristic_xml) = 0; // Called when heuristic either successfully considered for upload and // not send or uploaded. @@ -35,8 +40,10 @@ class AutoFillDownloadManager : public URLFetcher::Delegate { const std::string& form_signature) = 0; // Called when there was an error during the request. // |form_signature| - the signature of the requesting form. + // |request_type| - type of request that failed. // |http_error| - http error code. virtual void OnHeuristicsRequestError(const std::string& form_signature, + AutoFillRequestType request_type, int http_error) = 0; protected: virtual ~Observer() {} @@ -48,21 +55,52 @@ class AutoFillDownloadManager : public URLFetcher::Delegate { // |observer| - observer to notify on successful completion or error. void SetObserver(AutoFillDownloadManager::Observer *observer); - // Initiates request to AutoFill servers to download/upload heuristics + // Starts a query request to AutoFill servers. The observer is called with the + // list of the fields of all requested forms. + // |forms| - array of forms aggregated in this request. + bool StartQueryRequest(const ScopedVector<FormStructure>& forms); + + // Start upload request if necessary. The probability of request going + // over the wire are |positive_upload_rate_| if it was matched by + // AutoFill, |negative_download_rate_| otherwise. Observer will be called + // even if there was no actual trip over the wire. + // |form| - form sent in this request. + // |form_was_matched| - true if form was matched by the AutoFill. + bool StartUploadRequest(const FormStructure& form, bool form_was_matched); + + // Cancels pending request. + // |form_signature| - signature of the form being cancelled. Warning: + // for query request if more than one form sent in the request, all other + // forms will be cancelled as well. + // |request_type| - type of the request. + bool CancelRequest(const std::string& form_signature, + AutoFillRequestType request_type); + + void SetPositiveUploadRate(double rate) { + DCHECK(rate >= 0.0 && rate <= 1.0); + positive_upload_rate_ = rate; + } + + void SetNegativeUploadRate(double rate) { + DCHECK(rate >= 0.0 && rate <= 1.0); + negative_upload_rate_ = rate; + } + + private: + struct FormRequestData { + std::vector<std::string> form_signatures; + AutoFillRequestType request_type; + }; + + // Initiates request to AutoFill servers to download/upload heuristics. // |form_xml| - form structure XML to upload/download. - // |form_signature| - form signature hash. - // |query_data| - if true the data is queried and observer notified with new - // data, if available. If false heuristic data is uploaded to our servers. - // |form_was_matched| - if |query_data| is false indicates if the form was - // matched. Ignored otherwise. + // |request_data| - form signature hash(es) and indicator if it was a query. + // |request_data.query| - if true the data is queried and observer notified + // with new data, if available. If false heuristic data is uploaded to our + // servers. bool StartRequest(const std::string& form_xml, - const std::string& form_signature, - bool query_data, - bool form_was_matched); - - bool CancelRequest(const std::string& form_signature, bool query_data); + const FormRequestData& request_data); - protected: // URLFetcher::Delegate implementation: virtual void OnURLFetchComplete(const URLFetcher* source, const GURL& url, @@ -71,17 +109,20 @@ class AutoFillDownloadManager : public URLFetcher::Delegate { const ResponseCookies& cookies, const std::string& data); - private: - struct FormRequestData { - std::string form_signature; - bool query; - }; - // For each requested form for both query and upload we create a separate // request and save its info. As url fetcher is identified by its address // we use a map between fetchers and info. - std::map<URLFetcher *, FormRequestData> url_fetchers_; + std::map<URLFetcher*, FormRequestData> url_fetchers_; AutoFillDownloadManager::Observer *observer_; + + // Probability of the form upload. Between 0 (no upload) and 1 (upload all). + // |positive_upload_rate_| is for matched forms, + // |negative_upload_rate_| for non matched. + double positive_upload_rate_; + double negative_upload_rate_; + + // Needed for unit-test. + int fetcher_id_for_unittest_; }; #endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_DOWNLOAD_H_ diff --git a/chrome/browser/autofill/autofill_download_unittest.cc b/chrome/browser/autofill/autofill_download_unittest.cc new file mode 100644 index 0000000..92be846 --- /dev/null +++ b/chrome/browser/autofill/autofill_download_unittest.cc @@ -0,0 +1,240 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <list> + +#include "base/string_util.h" +#include "chrome/browser/autofill/autofill_download.h" +#include "chrome/browser/net/test_url_fetcher_factory.h" +#include "net/url_request/url_request_status.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/WebKit/chromium/public/WebInputElement.h" +#include "webkit/glue/form_field_values.h" + +using WebKit::WebInputElement; + +namespace { + +// This tests AutoFillDownloadManager. AutoFillDownloadTestHelper implements +// AutoFillDownloadManager::Observer and creates instance of +// AutoFillDownloadManager. Then it records responses to different initiated +// requests, which are verified later. To mock network requests +// TestURLFetcherFactory is used, which creates URLFetchers that do not +// go over the wire, but allow calling back HTTP responses directly. +// The responses in test are out of order and verify: successful query request, +// successful upload request, failed upload request. +class AutoFillDownloadTestHelper : public AutoFillDownloadManager::Observer { + public: + AutoFillDownloadTestHelper() { + download_manager.SetObserver(this); + } + ~AutoFillDownloadTestHelper() { + download_manager.SetObserver(NULL); + } + + // AutoFillDownloadManager::Observer overridables: + virtual void OnLoadedAutoFillHeuristics( + const std::vector<std::string>& form_signatures, + const std::string& heuristic_xml) { + ResponseData response; + for (size_t i = 0; i < form_signatures.size(); ++i) { + if (i) + response.signature += ","; + response.signature += form_signatures[i]; + } + response.response = heuristic_xml; + response.type_of_response = QUERY_SUCCESSFULL; + responses_.push_back(response); + }; + + virtual void OnUploadedAutoFillHeuristics(const std::string& form_signature) { + ResponseData response; + response.signature = form_signature; + response.type_of_response = UPLOAD_SUCCESSFULL; + responses_.push_back(response); + } + virtual void OnHeuristicsRequestError( + const std::string& form_signature, + AutoFillDownloadManager::AutoFillRequestType request_type, + int http_error) { + ResponseData response; + response.signature = form_signature; + response.error = http_error; + response.type_of_response = + request_type == AutoFillDownloadManager::REQUEST_QUERY ? + REQUEST_QUERY_FAILED : REQUEST_UPLOAD_FAILED; + responses_.push_back(response); + } + + enum TYPE_OF_RESPONSE { + QUERY_SUCCESSFULL, + UPLOAD_SUCCESSFULL, + REQUEST_QUERY_FAILED, + REQUEST_UPLOAD_FAILED, + }; + + struct ResponseData { + TYPE_OF_RESPONSE type_of_response; + int error; + std::string signature; + std::string response; + ResponseData() : type_of_response(REQUEST_QUERY_FAILED), error(0) { + } + }; + std::list<AutoFillDownloadTestHelper::ResponseData> responses_; + + AutoFillDownloadManager download_manager; +}; + +TEST(AutoFillDownloadTest, QueryAndUploadTest) { + MessageLoopForUI message_loop; + // Create and register factory. + AutoFillDownloadTestHelper helper; + TestURLFetcherFactory factory; + URLFetcher::set_factory(&factory); + + webkit_glue::FormFieldValues values; + values.method = ASCIIToUTF16("post"); + values.elements.push_back(webkit_glue::FormField(ASCIIToUTF16("username"), + ASCIIToUTF16("username"), + string16(), + ASCIIToUTF16("text"), + WebInputElement::Text)); + values.elements.push_back(webkit_glue::FormField(ASCIIToUTF16("email"), + ASCIIToUTF16("email"), + string16(), + ASCIIToUTF16("text"), + WebInputElement::Text)); + values.elements.push_back(webkit_glue::FormField(ASCIIToUTF16("email2"), + ASCIIToUTF16("email2"), + string16(), + ASCIIToUTF16("text"), + WebInputElement::Text)); + values.elements.push_back(webkit_glue::FormField(ASCIIToUTF16("password"), + ASCIIToUTF16("password"), + string16(), + ASCIIToUTF16("password"), + WebInputElement::Password)); + values.elements.push_back(webkit_glue::FormField(string16(), + ASCIIToUTF16("Submit"), + string16(), + ASCIIToUTF16("submit"), + WebInputElement::Submit)); + + FormStructure *form = new FormStructure(values); + ScopedVector<FormStructure> form_structures; + form_structures.push_back(form); + + values.elements.clear(); + values.elements.push_back(webkit_glue::FormField(ASCIIToUTF16("address"), + ASCIIToUTF16("address"), + string16(), + ASCIIToUTF16("text"), + WebInputElement::Text)); + values.elements.push_back(webkit_glue::FormField(ASCIIToUTF16("address2"), + ASCIIToUTF16("address2"), + string16(), + ASCIIToUTF16("text"), + WebInputElement::Text)); + values.elements.push_back(webkit_glue::FormField(ASCIIToUTF16("city"), + ASCIIToUTF16("address2"), + string16(), + ASCIIToUTF16("text"), + WebInputElement::Text)); + values.elements.push_back(webkit_glue::FormField(string16(), + ASCIIToUTF16("Submit"), + string16(), + ASCIIToUTF16("submit"), + WebInputElement::Submit)); + form = new FormStructure(values); + form_structures.push_back(form); + + // Request with id 0. + EXPECT_TRUE(helper.download_manager.StartQueryRequest(form_structures)); + // Set upload to 100% so requests happen. + helper.download_manager.SetPositiveUploadRate(1.0); + helper.download_manager.SetNegativeUploadRate(1.0); + // Request with id 1. + EXPECT_TRUE(helper.download_manager.StartUploadRequest(*(form_structures[0]), + true)); + // Request with id 2. + EXPECT_TRUE(helper.download_manager.StartUploadRequest(*(form_structures[1]), + false)); + + const char *responses[] = { + "<autofillqueryresponse>" + "<field autofilltype=\"0\" />" + "<field autofilltype=\"9\" />" + "<field autofilltype=\"0\" />" + "<field autofilltype=\"30\" />" + "<field autofilltype=\"31\" />" + "<field autofilltype=\"33\" />" + "</autofillqueryresponse>", + "<autofilluploadresponse positiveuploadrate=\"0.5\" " + "negativeuploadrate=\"0.3\"/>", + "<html></html>", + }; + + // Return them out of sequence. + TestURLFetcher* fetcher = factory.GetFetcherByID(1); + ASSERT_TRUE(fetcher); + fetcher->delegate()->OnURLFetchComplete(fetcher, GURL(), URLRequestStatus(), + 200, ResponseCookies(), + std::string(responses[1])); + fetcher = factory.GetFetcherByID(2); + ASSERT_TRUE(fetcher); + fetcher->delegate()->OnURLFetchComplete(fetcher, GURL(), URLRequestStatus(), + 404, ResponseCookies(), + std::string(responses[2])); + fetcher = factory.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + fetcher->delegate()->OnURLFetchComplete(fetcher, GURL(), URLRequestStatus(), + 200, ResponseCookies(), + std::string(responses[0])); + EXPECT_EQ(static_cast<size_t>(3), helper.responses_.size()); + + EXPECT_EQ(AutoFillDownloadTestHelper::UPLOAD_SUCCESSFULL, + helper.responses_.front().type_of_response); + EXPECT_EQ(0, helper.responses_.front().error); + EXPECT_EQ(form_structures[0]->FormSignature(), + helper.responses_.front().signature); + // Expected response on non-query request is an empty string. + EXPECT_EQ(std::string(""), helper.responses_.front().response); + helper.responses_.pop_front(); + + EXPECT_EQ(AutoFillDownloadTestHelper::REQUEST_UPLOAD_FAILED, + helper.responses_.front().type_of_response); + EXPECT_EQ(404, helper.responses_.front().error); + EXPECT_EQ(form_structures[1]->FormSignature(), + helper.responses_.front().signature); + // Expected response on non-query request is an empty string. + EXPECT_EQ(std::string(""), helper.responses_.front().response); + helper.responses_.pop_front(); + + EXPECT_EQ(helper.responses_.front().type_of_response, + AutoFillDownloadTestHelper::QUERY_SUCCESSFULL); + EXPECT_EQ(0, helper.responses_.front().error); + std::string signature(form_structures[0]->FormSignature()); + signature.append(","); + signature.append(form_structures[1]->FormSignature()); + EXPECT_EQ(signature, helper.responses_.front().signature); + EXPECT_EQ(responses[0], helper.responses_.front().response); + helper.responses_.pop_front(); + // Set upload to 0% so no new requests happen. + helper.download_manager.SetPositiveUploadRate(0.0); + helper.download_manager.SetNegativeUploadRate(0.0); + // No actual requests for the next two calls, as we set upload rate to 0%. + EXPECT_TRUE(helper.download_manager.StartUploadRequest(*(form_structures[0]), + true)); + EXPECT_TRUE(helper.download_manager.StartUploadRequest(*(form_structures[1]), + false)); + fetcher = factory.GetFetcherByID(4); + EXPECT_EQ(NULL, fetcher); + + // Make sure consumer of URLFetcher does the right thing. + URLFetcher::set_factory(NULL); +} + +} // namespace + diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc index 4ffdc20..e630d9e 100644 --- a/chrome/browser/autofill/autofill_manager.cc +++ b/chrome/browser/autofill/autofill_manager.cc @@ -90,15 +90,8 @@ void AutoFillManager::FormsSeen( FormStructure* form_structure = new FormStructure(*iter); DeterminePossibleFieldTypes(form_structure); form_structures_.push_back(form_structure); - std::string request_xml; - if (form_structure->IsAutoFillable() && - form_structure->EncodeUploadRequest(true, true, &request_xml)) { - download_manager_.StartRequest(request_xml, - form_structure->FormSignature(), - true, - false); - } } + download_manager_.StartQueryRequest(form_structures_); } bool AutoFillManager::GetAutoFillSuggestions( @@ -294,36 +287,44 @@ void AutoFillManager::Reset() { } void AutoFillManager::OnLoadedAutoFillHeuristics( - const std::string& form_signature, + const std::vector<std::string>& form_signatures, const std::string& heuristic_xml) { - for (ScopedVector<FormStructure>::iterator it = form_structures_.begin(); - it != form_structures_.end(); - ++it) { - if ((*it)->FormSignature() == form_signature) { - // Create a vector of AutoFillFieldTypes, - // to assign the parsed field types to. - std::vector<AutoFillFieldType> field_types; - UploadRequired upload_required = USE_UPLOAD_RATES; - - // Create a parser. - AutoFillQueryXmlParser parse_handler(&field_types, &upload_required); - buzz::XmlParser parser(&parse_handler); - parser.Parse(heuristic_xml.c_str(), heuristic_xml.length(), true); - if (parse_handler.succeeded()) { - DCHECK(field_types.size() == (*it)->field_count()); - if (field_types.size() == (*it)->field_count()) { - for (size_t i = 0; i < (*it)->field_count(); ++i) { - if (field_types[i] != NO_SERVER_DATA && - field_types[i] != UNKNOWN_TYPE) { - FieldTypeSet types = (*it)->field(i)->possible_types(); - types.insert(field_types[i]); - (*it)->set_possible_types(i, types); - } - } - } - return; + // Create a vector of AutoFillFieldTypes, + // to assign the parsed field types to. + std::vector<AutoFillFieldType> field_types; + UploadRequired upload_required = USE_UPLOAD_RATES; + + // Create a parser. + AutoFillQueryXmlParser parse_handler(&field_types, &upload_required); + buzz::XmlParser parser(&parse_handler); + parser.Parse(heuristic_xml.c_str(), heuristic_xml.length(), true); + if (!parse_handler.succeeded()) { + return; + } + + // For multiple forms requested, returned field types are in one array. + // |field_shift| indicates start of the fields for current form. + size_t field_shift = 0; + // form_signatures should mirror form_structures_ unless new request is + // initiated. So if there is a discrepancy we just ignore data and return. + ScopedVector<FormStructure>::iterator it_forms; + std::vector<std::string>::const_iterator it_signatures; + for (it_forms = form_structures_.begin(), + it_signatures = form_signatures.begin(); + it_forms != form_structures_.end() && + it_signatures != form_signatures.end() && + (*it_forms)->FormSignature() == *it_signatures; + ++it_forms, ++it_signatures) { + DCHECK(field_types.size() - field_shift >= (*it_forms)->field_count()); + for (size_t i = 0; i < (*it_forms)->field_count(); ++i) { + if (field_types[i + field_shift] != NO_SERVER_DATA && + field_types[i + field_shift] != UNKNOWN_TYPE) { + FieldTypeSet types = (*it_forms)->field(i)->possible_types(); + types.insert(field_types[i]); + (*it_forms)->set_possible_types(i, types); } } + field_shift += (*it_forms)->field_count(); } } @@ -332,7 +333,9 @@ void AutoFillManager::OnUploadedAutoFillHeuristics( } void AutoFillManager::OnHeuristicsRequestError( - const std::string& form_signature, int http_error) { + const std::string& form_signature, + AutoFillDownloadManager::AutoFillRequestType request_type, + int http_error) { } void AutoFillManager::DeterminePossibleFieldTypes( @@ -363,16 +366,10 @@ void AutoFillManager::HandleSubmit() { } void AutoFillManager::UploadFormData() { - std::string xml; - bool ok = upload_form_structure_->EncodeUploadRequest(false, false, &xml); - DCHECK(ok); - // TODO(georgey): enable upload request when we make sure that our data is in // line with toolbar data: - // download_manager_.StartRequest(xml, - // upload_form_structure_->FormSignature(), - // false, - // form_is_autofilled); + // download_manager_.StartUploadRequest(upload_form_structure_, + // form_is_autofilled); } bool AutoFillManager::IsAutoFillEnabled() { diff --git a/chrome/browser/autofill/autofill_manager.h b/chrome/browser/autofill/autofill_manager.h index f56a5f2..d89d5da 100644 --- a/chrome/browser/autofill/autofill_manager.h +++ b/chrome/browser/autofill/autofill_manager.h @@ -77,11 +77,14 @@ class AutoFillManager : public RenderViewHostDelegate::AutoFill, virtual void Reset(); // AutoFillDownloadManager::Observer implementation: - virtual void OnLoadedAutoFillHeuristics(const std::string& form_signature, - const std::string& heuristic_xml); + virtual void OnLoadedAutoFillHeuristics( + const std::vector<std::string>& form_signatures, + const std::string& heuristic_xml); virtual void OnUploadedAutoFillHeuristics(const std::string& form_signature); - virtual void OnHeuristicsRequestError(const std::string& form_signature, - int http_error); + virtual void OnHeuristicsRequestError( + const std::string& form_signature, + AutoFillDownloadManager::AutoFillRequestType request_type, + int http_error); // Uses heuristics and existing personal data to determine the possible field // types. diff --git a/chrome/browser/autofill/form_structure.cc b/chrome/browser/autofill/form_structure.cc index dd9cfaa..f96e5272 100644 --- a/chrome/browser/autofill/form_structure.cc +++ b/chrome/browser/autofill/form_structure.cc @@ -36,6 +36,8 @@ const char* const kAttributeAutoFillType = "autofilltype"; // The only form control type we handle currently. const char* const kControlTypeText = "text"; +// This type is required for AutoFill servers. +const char* const kControlTypeSingleSelect = "select-one"; // The number of fillable fields necessary for a form to be fillable. const size_t kRequiredFillableFields = 3; @@ -66,9 +68,18 @@ FormStructure::FormStructure(const FormFieldValues& values) std::vector<webkit_glue::FormField>::const_iterator field; for (field = values.elements.begin(); field != values.elements.end(); field++) { + // Add all form fields (including with empty names) to signature. + // This is requirement for AutoFill servers. + bool is_text_control = LowerCaseEqualsASCII(field->form_control_type(), + kControlTypeText); + if (is_text_control || LowerCaseEqualsASCII(field->form_control_type(), + kControlTypeSingleSelect)) { + form_signature_field_names_.append("&"); + form_signature_field_names_.append(UTF16ToUTF8(field->name())); + } // We currently only handle text fields. This prevents us from thinking we // can autofill other types of controls, e.g., select, password, hidden. - if (!LowerCaseEqualsASCII(field->form_control_type(), kControlTypeText)) + if (!is_text_control) continue; // Generate a unique name for this field by appending a counter to the name. @@ -90,65 +101,61 @@ FormStructure::FormStructure(const FormFieldValues& values) } bool FormStructure::EncodeUploadRequest(bool auto_fill_used, - bool query, std::string* encoded_xml) const { bool auto_fillable = IsAutoFillable(); DCHECK(auto_fillable); // Caller should've checked for search pages. if (!auto_fillable) return false; - buzz::XmlElement autofil_request_xml(query ? buzz::QName("autofillquery") : - buzz::QName("autofillupload")); - buzz::XmlElement *encompassing_xml_element = &autofil_request_xml; - if (query) - encompassing_xml_element = new buzz::XmlElement(buzz::QName("form")); + buzz::XmlElement autofil_request_xml(buzz::QName("autofillupload")); - // Attributes for the <autofillupload>/<autofillquery> element. + // Attributes for the <autofillupload> element. // // TODO(jhawkins): Work with toolbar devs to make a spec for autofill clients. // For now these values are hacked from the toolbar code. autofil_request_xml.SetAttr(buzz::QName(kAttributeClientVersion), "6.1.1715.1442/en (GGLL)"); - encompassing_xml_element->SetAttr(query ? buzz::QName(kAttributeSignature) : - buzz::QName(kAttributeFormSignature), - FormSignature()); + autofil_request_xml.SetAttr(buzz::QName(kAttributeFormSignature), + FormSignature()); - if (!query) { - autofil_request_xml.SetAttr(buzz::QName(kAttributeAutoFillUsed), - auto_fill_used ? "true" : "false"); + autofil_request_xml.SetAttr(buzz::QName(kAttributeAutoFillUsed), + auto_fill_used ? "true" : "false"); - // TODO(jhawkins): Hook this up to the personal data manager. - // personaldata_manager_->GetDataPresent(); - autofil_request_xml.SetAttr(buzz::QName(kAttributeDataPresent), ""); - } + // TODO(jhawkins): Hook this up to the personal data manager. + // personaldata_manager_->GetDataPresent(); + autofil_request_xml.SetAttr(buzz::QName(kAttributeDataPresent), ""); - // Add the child nodes for the form fields. - for (size_t index = 0; index < field_count(); index++) { - const AutoFillField* field = fields_[index]; - if (!query) { - FieldTypeSet types = field->possible_types(); - for (FieldTypeSet::const_iterator type = types.begin(); - type != types.end(); type++) { - buzz::XmlElement *field_element = new buzz::XmlElement( - buzz::QName(kXMLElementField)); + EncodeFormRequest(FormStructure::UPLOAD, &autofil_request_xml); + + // Obtain the XML structure as a string. + *encoded_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; + *encoded_xml += autofil_request_xml.Str().c_str(); + + return true; +} + +bool FormStructure::EncodeQueryRequest(const ScopedVector<FormStructure>& forms, + std::string* encoded_xml) { + buzz::XmlElement autofil_request_xml(buzz::QName("autofillquery")); + // Attributes for the <autofillquery> element. + // + // TODO(jhawkins): Work with toolbar devs to make a spec for autofill clients. + // For now these values are hacked from the toolbar code. + autofil_request_xml.SetAttr(buzz::QName(kAttributeClientVersion), + "6.1.1715.1442/en (GGLL)"); + for (ScopedVector<FormStructure>::const_iterator it = forms.begin(); + it != forms.end(); + ++it) { + buzz::XmlElement* encompassing_xml_element = + new buzz::XmlElement(buzz::QName("form")); + encompassing_xml_element->SetAttr(buzz::QName(kAttributeSignature), + (*it)->FormSignature()); + + (*it)->EncodeFormRequest(FormStructure::QUERY, encompassing_xml_element); - field_element->SetAttr(buzz::QName(kAttributeSignature), - field->FieldSignature()); - field_element->SetAttr(buzz::QName(kAttributeAutoFillType), - IntToString(*type)); - encompassing_xml_element->AddElement(field_element); - } - } else { - buzz::XmlElement *field_element = new buzz::XmlElement( - buzz::QName(kXMLElementField)); - field_element->SetAttr(buzz::QName(kAttributeSignature), - field->FieldSignature()); - encompassing_xml_element->AddElement(field_element); - } - } - if (query) autofil_request_xml.AddElement(encompassing_xml_element); + } // Obtain the XML structure as a string. *encoded_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; @@ -185,7 +192,9 @@ void FormStructure::GetHeuristicAutoFillTypes() { } std::string FormStructure::FormSignature() const { - std::string form_string = target_url_.host() + + std::string form_string = target_url_.scheme() + + "://" + + target_url_.host() + "&" + form_name_ + form_signature_field_names_; @@ -253,3 +262,36 @@ void FormStructure::GetHeuristicFieldInfo(FieldTypeMap* field_type_map) { DCHECK(ok); } } + +bool FormStructure::EncodeFormRequest( + FormStructure::EncodeRequestType request_type, + buzz::XmlElement* encompassing_xml_element) const { + if (!field_count()) // Nothing to add. + return false; + // Add the child nodes for the form fields. + for (size_t index = 0; index < field_count(); index++) { + const AutoFillField* field = fields_[index]; + if (request_type == FormStructure::UPLOAD) { + FieldTypeSet types = field->possible_types(); + for (FieldTypeSet::const_iterator type = types.begin(); + type != types.end(); type++) { + buzz::XmlElement *field_element = new buzz::XmlElement( + buzz::QName(kXMLElementField)); + + field_element->SetAttr(buzz::QName(kAttributeSignature), + field->FieldSignature()); + field_element->SetAttr(buzz::QName(kAttributeAutoFillType), + IntToString(*type)); + encompassing_xml_element->AddElement(field_element); + } + } else { + buzz::XmlElement *field_element = new buzz::XmlElement( + buzz::QName(kXMLElementField)); + field_element->SetAttr(buzz::QName(kAttributeSignature), + field->FieldSignature()); + encompassing_xml_element->AddElement(field_element); + } + } + return true; +} + diff --git a/chrome/browser/autofill/form_structure.h b/chrome/browser/autofill/form_structure.h index 3802aed..bb20885 100644 --- a/chrome/browser/autofill/form_structure.h +++ b/chrome/browser/autofill/form_structure.h @@ -14,10 +14,14 @@ #include "chrome/browser/autofill/field_types.h" #include "googleurl/src/gurl.h" +namespace buzz { + class XmlElement; +} // namespace buzz + namespace webkit_glue { struct FormData; class FormFieldValues; -} +} // namespace webkit_glue enum RequestMethod { GET, @@ -36,11 +40,18 @@ class FormStructure { public: explicit FormStructure(const webkit_glue::FormFieldValues& values); - // Encodes the XML query or upload request from this FormStructure. - // |query| - true means request is a query, upload otherwise. - bool EncodeUploadRequest(bool auto_fill_used, bool query, + // Encodes the XML upload request from this FormStructure. + bool EncodeUploadRequest(bool auto_fill_used, std::string* encoded_xml) const; + // Encodes the XML query request for the set of forms. + // All fields are returned in one XML. For example, there are three forms, + // with 2, 4, and 3 fields. The returned XML would have type info for 9 + // fields, first two of which would be for the first form, next 4 for the + // second, and the rest is for the third. + static bool EncodeQueryRequest(const ScopedVector<FormStructure>& forms, + std::string* encoded_xml); + // Runs several heuristics against the form fields to determine their possible // types. void GetHeuristicAutoFillTypes(); @@ -71,9 +82,19 @@ class FormStructure { bool operator!=(const webkit_glue::FormData& form) const; private: + enum EncodeRequestType { + QUERY, + UPLOAD, + }; + // Associates the field with the heuristic type for each of the field views. void GetHeuristicFieldInfo(FieldTypeMap* field_types_map); + // Adds form info to |encompassing_xml_element|. |request_type| indicates if + // it is a query or upload. + bool EncodeFormRequest(EncodeRequestType request_type, + buzz::XmlElement* encompassing_xml_element) const; + // The name of the form. // TODO(jhawkins): string16 std::string form_name_; |