summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-18 01:02:43 +0000
committerjhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-18 01:02:43 +0000
commitec64212b924d80d3ba5da864812b8dea1132854e (patch)
tree7a381945a0210c5fb55c60a059a94613b840473f
parente06d95c472709c146248a7cfe0a4bf2bc40942d3 (diff)
downloadchromium_src-ec64212b924d80d3ba5da864812b8dea1132854e.zip
chromium_src-ec64212b924d80d3ba5da864812b8dea1132854e.tar.gz
chromium_src-ec64212b924d80d3ba5da864812b8dea1132854e.tar.bz2
Implement AutoFillXmlParser, used to parse responses from the AutoFill server.
BUG=none TEST=AutoFillQueryXmlParserTest Review URL: http://codereview.chromium.org/1014008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41904 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/DEPS1
-rw-r--r--chrome/browser/autofill/autofill_xml_parser.cc131
-rw-r--r--chrome/browser/autofill/autofill_xml_parser.h133
-rw-r--r--chrome/browser/autofill/autofill_xml_parser_unittest.cc138
-rw-r--r--chrome/browser/autofill/form_structure.h6
-rwxr-xr-xchrome/chrome_browser.gypi3
-rw-r--r--chrome/chrome_tests.gypi3
7 files changed, 415 insertions, 0 deletions
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index f128afb..66f04b7 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -20,6 +20,7 @@ include_rules = [
"+libxml", # For search engine definition parsing.
"+media/audio", # Chrome's lightweight audio library.
"+media/base",
+ "+third_party/expat",
"+third_party/sqlite",
"+third_party/libevent", # For the remote V8 debugging server
"+third_party/cld",
diff --git a/chrome/browser/autofill/autofill_xml_parser.cc b/chrome/browser/autofill/autofill_xml_parser.cc
new file mode 100644
index 0000000..2d84fa4
--- /dev/null
+++ b/chrome/browser/autofill/autofill_xml_parser.cc
@@ -0,0 +1,131 @@
+// 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 "chrome/browser/autofill/autofill_xml_parser.h"
+
+#include <string>
+#include <vector>
+
+#include "chrome/browser/autofill/autofill_type.h"
+#include "third_party/libjingle/files/talk/xmllite/qname.h"
+
+AutoFillXmlParser::AutoFillXmlParser()
+ : succeeded_(true) {
+}
+
+void AutoFillXmlParser::CharacterData(
+ buzz::XmlParseContext* context, const char* text, int len) {
+}
+
+void AutoFillXmlParser::EndElement(buzz::XmlParseContext* context,
+ const char* name) {
+}
+
+void AutoFillXmlParser::Error(buzz::XmlParseContext* context,
+ XML_Error error_code) {
+ succeeded_ = false;
+}
+
+AutoFillQueryXmlParser::AutoFillQueryXmlParser(
+ std::vector<AutoFillFieldType>* field_types,
+ UploadRequired* upload_required)
+ : field_types_(field_types),
+ upload_required_(upload_required) {
+ DCHECK(upload_required_);
+}
+
+void AutoFillQueryXmlParser::StartElement(buzz::XmlParseContext* context,
+ const char* name,
+ const char** attrs) {
+ buzz::QName qname = context->ResolveQName(name, false);
+ const std::string &element = qname.LocalPart();
+ if (element.compare("autofillqueryresponse") == 0) {
+ // Check for the upload required attribute. If it's not present, we use the
+ // default upload rates.
+ *upload_required_ = USE_UPLOAD_RATES;
+ if (*attrs) {
+ buzz::QName attribute_qname = context->ResolveQName(attrs[0], true);
+ const std::string &attribute_name = attribute_qname.LocalPart();
+ if (attribute_name.compare("uploadrequired") == 0) {
+ if (strcmp(attrs[1], "true") == 0)
+ *upload_required_ = UPLOAD_REQUIRED;
+ else if (strcmp(attrs[1], "false") == 0)
+ *upload_required_ = UPLOAD_NOT_REQUIRED;
+ }
+ }
+ } else if (element.compare("field") == 0) {
+ if (!attrs[0]) {
+ // Missing the "autofilltype" attribute, abort.
+ context->RaiseError(XML_ERROR_ABORTED);
+ return;
+ }
+
+ // Determine the field type from the attribute value. There should be one
+ // attribute (autofilltype) with an integer value.
+ AutoFillFieldType field_type = UNKNOWN_TYPE;
+ buzz::QName attribute_qname = context->ResolveQName(attrs[0], true);
+ const std::string &attribute_name = attribute_qname.LocalPart();
+
+ if (attribute_name.compare("autofilltype") == 0) {
+ int value = GetIntValue(context, attrs[1]);
+ field_type = static_cast<AutoFillFieldType>(value);
+ if (field_type < 0 || field_type > MAX_VALID_FIELD_TYPE) {
+ field_type = NO_SERVER_DATA;
+ }
+ }
+
+ // Record this field type.
+ field_types_->push_back(field_type);
+ }
+}
+
+int AutoFillQueryXmlParser::GetIntValue(buzz::XmlParseContext* context,
+ const char* attribute) {
+ char* attr_end = NULL;
+ int value = strtol(attribute, &attr_end, 10);
+ if (attr_end != NULL && attr_end == attribute) {
+ context->RaiseError(XML_ERROR_SYNTAX);
+ return 0;
+ }
+ return value;
+}
+
+AutoFillUploadXmlParser::AutoFillUploadXmlParser(double* positive_upload_rate,
+ double* negative_upload_rate)
+ : positive_upload_rate_(positive_upload_rate),
+ negative_upload_rate_(negative_upload_rate) {
+ DCHECK(positive_upload_rate_);
+ DCHECK(negative_upload_rate_);
+}
+
+void AutoFillUploadXmlParser::StartElement(buzz::XmlParseContext* context,
+ const char* name,
+ const char** attrs) {
+ buzz::QName qname = context->ResolveQName(name, false);
+ const std::string &element = qname.LocalPart();
+ if (element.compare("autofilluploadresponse") == 0) {
+ // Loop over all attributes to get the upload rates.
+ while (*attrs) {
+ buzz::QName attribute_qname = context->ResolveQName(attrs[0], true);
+ const std::string &attribute_name = attribute_qname.LocalPart();
+ if (attribute_name.compare("positiveuploadrate") == 0) {
+ *positive_upload_rate_ = GetDoubleValue(context, attrs[1]);
+ } else if (attribute_name.compare("negativeuploadrate") == 0) {
+ *negative_upload_rate_ = GetDoubleValue(context, attrs[1]);
+ }
+ attrs += 2; // We peeked at attrs[0] and attrs[1], skip past both.
+ }
+ }
+}
+
+double AutoFillUploadXmlParser::GetDoubleValue(buzz::XmlParseContext* context,
+ const char* attribute) {
+ char* attr_end = NULL;
+ double value = strtod(attribute, &attr_end);
+ if (attr_end != NULL && attr_end == attribute) {
+ context->RaiseError(XML_ERROR_SYNTAX);
+ return 0.0;
+ }
+ return value;
+}
diff --git a/chrome/browser/autofill/autofill_xml_parser.h b/chrome/browser/autofill/autofill_xml_parser.h
new file mode 100644
index 0000000..d54dc67
--- /dev/null
+++ b/chrome/browser/autofill/autofill_xml_parser.h
@@ -0,0 +1,133 @@
+// 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.
+
+#ifndef CHROME_BROWSER_AUTOFILL_AUTOFILL_XML_PARSER_H_
+#define CHROME_BROWSER_AUTOFILL_AUTOFILL_XML_PARSER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "chrome/browser/autofill/field_types.h"
+#include "chrome/browser/autofill/form_structure.h"
+#include "third_party/expat/files/lib/expat.h"
+#include "third_party/libjingle/files/talk/xmllite/xmlparser.h"
+
+enum UploadRequired;
+
+// The base class that contains common functionality between
+// AutoFillQueryXmlParser and AutoFillUploadXmlParser.
+class AutoFillXmlParser : public buzz::XmlParseHandler {
+ public:
+ AutoFillXmlParser();
+
+ // Returns true if no parsing errors were encountered.
+ bool succeeded() const { return succeeded_; }
+
+ private:
+ // A callback for the end of an </element>, called by Expat.
+ // |context| is a parsing context used to resolve element/attribute names.
+ // |name| is the name of the element.
+ virtual void EndElement(buzz::XmlParseContext* context, const char* name);
+
+ // The callback for character data between tags (<element>text...</element>).
+ // |context| is a parsing context used to resolve element/attribute names.
+ // |text| is a pointer to the beginning of character data (not null
+ // terminated).
+ // |len| is the length of the string pointed to by text.
+ virtual void CharacterData(buzz::XmlParseContext* context, const char* text,
+ int len);
+
+ // The callback for parsing errors.
+ // |context| is a parsing context used to resolve names.
+ // |error_code| is a code representing the parsing error.
+ virtual void Error(buzz::XmlParseContext* context, XML_Error error_code);
+
+ // True if parsing succeeded.
+ bool succeeded_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutoFillXmlParser);
+};
+
+// The XML parse handler for parsing AutoFill query responses. A typical
+// response looks like:
+//
+// <autofillqueryresponse>
+// <field autofilltype="0" />
+// <field autofilltype="1" />
+// <field autofilltype="3" />
+// <field autofilltype="2" />
+// </autofillqueryresponse>
+//
+// Fields are returned in the same order they were sent to the server.
+// autofilltype: The server's guess at what type of field this is. 0 is
+// unknown, other types are documented in chrome/browser/autofill/field_types.h.
+class AutoFillQueryXmlParser : public AutoFillXmlParser {
+ public:
+ AutoFillQueryXmlParser(std::vector<AutoFillFieldType>* field_types,
+ UploadRequired* upload_required);
+
+ private:
+ // A callback for the beginning of a new <element>, called by Expat.
+ // |context| is a parsing context used to resolve element/attribute names.
+ // |name| is the name of the element.
+ // |attrs| is the list of attributes (names and values) for the element.
+ virtual void StartElement(buzz::XmlParseContext* context, const char* name,
+ const char** attrs);
+
+ // A helper function to retrieve integer values from strings. Raises an
+ // XML parse error if it fails.
+ // |context| is the current parsing context.
+ // |value| is the string to convert.
+ int GetIntValue(buzz::XmlParseContext* context, const char* attribute);
+
+ // The parsed field types.
+ std::vector<AutoFillFieldType>* field_types_;
+
+ // A flag indicating whether the client should upload AutoFill data when this
+ // form is submitted.
+ UploadRequired* upload_required_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutoFillQueryXmlParser);
+};
+
+// The XML parser for handling AutoFill upload responses. Typical upload
+// responses look like:
+//
+// <autofilluploadresponse negativeuploadrate="0.00125" positiveuploadrate="1"/>
+//
+// The positive upload rate is the percentage of uploads to send to the server
+// when something in the users profile matches what they have entered in a form.
+// The negative upload rate is the percentage of uploads to send when nothing in
+// the form matches what's in the users profile.
+// The negative upload rate is typically much lower than the positive upload
+// rate.
+class AutoFillUploadXmlParser : public buzz::XmlParseHandler {
+ public:
+ AutoFillUploadXmlParser(double* positive_upload_rate,
+ double* negative_upload_rate);
+
+ private:
+ // A callback for the beginning of a new <element>, called by Expat.
+ // |context| is a parsing context used to resolve element/attribute names.
+ // |name| is the name of the element.
+ // |attrs| is the list of attributes (names and values) for the element.
+ virtual void StartElement(buzz::XmlParseContext* context, const char* name,
+ const char** attrs);
+
+ // A helper function to retrieve double values from strings. Raises an XML
+ // parse error if it fails.
+ // |context| is the current parsing context.
+ // |value| is the string to convert.
+ double GetDoubleValue(buzz::XmlParseContext* context, const char* attribute);
+
+ // True if parsing succeeded.
+ bool succeeded_;
+
+ double* positive_upload_rate_;
+ double* negative_upload_rate_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutoFillUploadXmlParser);
+};
+
+#endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_XML_PARSER_H_
diff --git a/chrome/browser/autofill/autofill_xml_parser_unittest.cc b/chrome/browser/autofill/autofill_xml_parser_unittest.cc
new file mode 100644
index 0000000..7e1e55c
--- /dev/null
+++ b/chrome/browser/autofill/autofill_xml_parser_unittest.cc
@@ -0,0 +1,138 @@
+// 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 <string>
+#include <vector>
+
+#include "chrome/browser/autofill/autofill_xml_parser.h"
+#include "chrome/browser/autofill/field_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libjingle/files/talk/xmllite/xmlparser.h"
+
+namespace {
+
+TEST(AutoFillQueryXmlParserTest, BasicQuery) {
+ // An XML string representing a basic query response.
+ std::string xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"0\" />"
+ "<field autofilltype=\"1\" />"
+ "<field autofilltype=\"3\" />"
+ "<field autofilltype=\"2\" />"
+ "</autofillqueryresponse>";
+
+ // 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(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler.succeeded());
+ EXPECT_EQ(upload_required, USE_UPLOAD_RATES);
+ ASSERT_EQ(4U, field_types.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_types[0]);
+ EXPECT_EQ(UNKNOWN_TYPE, field_types[1]);
+ EXPECT_EQ(NAME_FIRST, field_types[2]);
+ EXPECT_EQ(EMPTY_TYPE, field_types[3]);
+}
+
+// Test parsing the upload required attribute.
+TEST(AutoFillQueryXmlParserTest, TestUploadRequired) {
+ std::vector<AutoFillFieldType> field_types;
+ UploadRequired upload_required = USE_UPLOAD_RATES;
+
+ std::string xml = "<autofillqueryresponse uploadrequired=\"true\">"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>";
+
+ scoped_ptr<AutoFillQueryXmlParser> parse_handler(
+ new AutoFillQueryXmlParser(&field_types, &upload_required));
+ scoped_ptr<buzz::XmlParser> parser(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(UPLOAD_REQUIRED, upload_required);
+ ASSERT_EQ(1U, field_types.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_types[0]);
+
+ field_types.clear();
+ xml = "<autofillqueryresponse uploadrequired=\"false\">"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>";
+
+ parse_handler.reset(
+ new AutoFillQueryXmlParser(&field_types, &upload_required));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(upload_required, UPLOAD_NOT_REQUIRED);
+ ASSERT_EQ(1U, field_types.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_types[0]);
+
+ field_types.clear();
+ xml = "<autofillqueryresponse uploadrequired=\"bad_value\">"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>";
+
+ parse_handler.reset(
+ new AutoFillQueryXmlParser(&field_types, &upload_required));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(upload_required, USE_UPLOAD_RATES);
+ ASSERT_EQ(1U, field_types.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_types[0]);
+}
+
+// Test badly formed XML queries.
+TEST(AutoFillQueryXmlParserTest, ParseErrors) {
+ std::vector<AutoFillFieldType> field_types;
+ UploadRequired upload_required = USE_UPLOAD_RATES;
+
+ // Test no AutoFill type.
+ std::string xml = "<autofillqueryresponse>"
+ "<field/>"
+ "</autofillqueryresponse>";
+
+ scoped_ptr<AutoFillQueryXmlParser> parse_handler(
+ new AutoFillQueryXmlParser(&field_types, &upload_required));
+ scoped_ptr<buzz::XmlParser> parser(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_FALSE(parse_handler->succeeded());
+ EXPECT_EQ(upload_required, USE_UPLOAD_RATES);
+ ASSERT_EQ(0U, field_types.size());
+
+ // Test an incorrect AutoFill type.
+ xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"307\"/>"
+ "</autofillqueryresponse>";
+
+ parse_handler.reset(
+ new AutoFillQueryXmlParser(&field_types, &upload_required));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(upload_required, USE_UPLOAD_RATES);
+ ASSERT_EQ(1U, field_types.size());
+ // AutoFillType was out of range and should be set to NO_SERVER_DATA.
+ EXPECT_EQ(NO_SERVER_DATA, field_types[0]);
+
+ // Test an incorrect AutoFill type.
+ field_types.clear();
+ xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"No Type\"/>"
+ "</autofillqueryresponse>";
+
+ // Parse fails but an entry is still added to field_types.
+ parse_handler.reset(
+ new AutoFillQueryXmlParser(&field_types, &upload_required));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_FALSE(parse_handler->succeeded());
+ EXPECT_EQ(upload_required, USE_UPLOAD_RATES);
+ ASSERT_EQ(1U, field_types.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_types[0]);
+}
+
+} // namespace
diff --git a/chrome/browser/autofill/form_structure.h b/chrome/browser/autofill/form_structure.h
index 6c41203..4376987 100644
--- a/chrome/browser/autofill/form_structure.h
+++ b/chrome/browser/autofill/form_structure.h
@@ -24,6 +24,12 @@ enum RequestMethod {
POST
};
+enum UploadRequired {
+ UPLOAD_NOT_REQUIRED,
+ UPLOAD_REQUIRED,
+ USE_UPLOAD_RATES
+};
+
// FormStructure stores a single HTML form together with the values entered
// in the fields along with additional information needed by AutoFill.
class FormStructure {
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 6cbd8b1..9560f97 100755
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -22,6 +22,7 @@
'../printing/printing.gyp:printing',
'../skia/skia.gyp:skia',
'../third_party/bzip2/bzip2.gyp:bzip2',
+ '../third_party/expat/expat.gyp:expat',
'../third_party/icu/icu.gyp:icui18n',
'../third_party/icu/icu.gyp:icuuc',
'../third_party/libjingle/libjingle.gyp:libjingle',
@@ -132,6 +133,8 @@
'browser/autofill/autofill_text_field_mac.mm',
'browser/autofill/autofill_type.cc',
'browser/autofill/autofill_type.h',
+ 'browser/autofill/autofill_xml_parser.cc',
+ 'browser/autofill/autofill_xml_parser.h',
'browser/autofill/billing_address.h',
'browser/autofill/contact_info.cc',
'browser/autofill/contact_info.h',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index ad74905..1229104 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -507,8 +507,10 @@
'../testing/gtest.gyp:gtest',
'../third_party/bzip2/bzip2.gyp:bzip2',
'../third_party/cld/cld.gyp:cld',
+ '../third_party/expat/expat.gyp:expat',
'../third_party/icu/icu.gyp:icui18n',
'../third_party/icu/icu.gyp:icuuc',
+ '../third_party/libjingle/libjingle.gyp:libjingle',
'../third_party/libxml/libxml.gyp:libxml',
'../third_party/npapi/npapi.gyp:npapi',
'../third_party/WebKit/WebKit/chromium/WebKit.gyp:webkit',
@@ -548,6 +550,7 @@
'browser/autofill/autofill_field_unittest.cc',
'browser/autofill/autofill_infobar_delegate_unittest.cc',
'browser/autofill/autofill_profile_unittest.cc',
+ 'browser/autofill/autofill_xml_parser_unittest.cc',
'browser/autofill/billing_address_unittest.cc',
'browser/autofill/credit_card_unittest.cc',
'browser/autofill/form_structure_unittest.cc',