diff options
-rw-r--r-- | chrome/browser/DEPS | 1 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_xml_parser.cc | 131 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_xml_parser.h | 133 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_xml_parser_unittest.cc | 138 | ||||
-rw-r--r-- | chrome/browser/autofill/form_structure.h | 6 | ||||
-rwxr-xr-x | chrome/chrome_browser.gypi | 3 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 3 |
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', |