summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/autofill/autofill_download.cc122
-rw-r--r--chrome/browser/autofill/autofill_download.h83
-rw-r--r--chrome/browser/autofill/autofill_download_unittest.cc240
-rw-r--r--chrome/browser/autofill/autofill_manager.cc85
-rw-r--r--chrome/browser/autofill/autofill_manager.h11
-rw-r--r--chrome/browser/autofill/form_structure.cc128
-rw-r--r--chrome/browser/autofill/form_structure.h29
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_;