Index: components/autofill/core/browser/form_structure.cc |
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc |
index 1c7be1347e14e70e83d4b8a9e4dbb8ca93e548fc..351acbcc6ba6735a0352dbc776cb34aabcff5337 100644 |
--- a/components/autofill/core/browser/form_structure.cc |
+++ b/components/autofill/core/browser/form_structure.cc |
@@ -4,6 +4,7 @@ |
#include "components/autofill/core/browser/form_structure.h" |
+#include <map> |
#include <utility> |
#include "base/basictypes.h" |
@@ -20,6 +21,7 @@ |
#include "base/strings/utf_string_conversions.h" |
#include "base/time/time.h" |
#include "components/autofill/core/browser/autofill_metrics.h" |
+#include "components/autofill/core/browser/autofill_server_field_info.h" |
#include "components/autofill/core/browser/autofill_type.h" |
#include "components/autofill/core/browser/autofill_xml_parser.h" |
#include "components/autofill/core/browser/field_types.h" |
@@ -31,8 +33,8 @@ |
#include "components/autofill/core/common/form_field_data_predictions.h" |
#include "components/rappor/rappor_service.h" |
#include "components/rappor/rappor_utils.h" |
+#include "third_party/libxml/chromium/libxml_utils.h" |
#include "third_party/re2/re2/re2.h" |
-#include "third_party/webrtc/libjingle/xmllite/xmlelement.h" |
namespace autofill { |
namespace { |
@@ -54,7 +56,6 @@ const char kAttributeControlType[] = "type"; |
const char kAttributeAutocomplete[] = "autocomplete"; |
const char kAttributeLoginFormSignature[] = "loginformsignature"; |
const char kClientVersion[] = "6.1.1715.1442/en (GGLL)"; |
-const char kXMLDeclaration[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; |
const char kXMLElementAutofillQuery[] = "autofillquery"; |
const char kXMLElementAutofillUpload[] = "autofillupload"; |
const char kXMLElementFieldAssignments[] = "fieldassignments"; |
@@ -115,75 +116,86 @@ std::string EncodeFieldTypes(const ServerFieldTypeSet& available_field_types) { |
return data_presence; |
} |
-// Helper for |EncodeFormRequest()| and |EncodeFieldForUpload| that returns an |
-// XmlElement for the given field in query xml, and also add it to the parent |
-// XmlElement. |
-buzz::XmlElement* EncodeFieldForQuery(const AutofillField& field, |
- buzz::XmlElement* parent) { |
- buzz::XmlElement* field_element = new buzz::XmlElement( |
- buzz::QName(kXMLElementField)); |
- field_element->SetAttr(buzz::QName(kAttributeSignature), |
- field.FieldSignature()); |
+// Assumes that |xml_writer| has just started an element with name |
+// |kXMLElementField|, and adds some field attributes based on |field|. Returns |
+// true on success, false on failure. |
+bool EncodeFieldForQuery(const AutofillField& field, XmlWriter* xml_writer) { |
+ if (!xml_writer->AddAttribute(kAttributeSignature, field.FieldSignature())) |
+ return false; |
if (IsAutofillFieldMetadataEnabled()) { |
if (!field.name.empty()) { |
- field_element->SetAttr(buzz::QName(kAttributeName), |
- base::UTF16ToUTF8(field.name)); |
+ if (!xml_writer->AddAttribute(kAttributeName, |
+ base::UTF16ToUTF8(field.name))) |
+ return false; |
} |
- field_element->SetAttr(buzz::QName(kAttributeControlType), |
- field.form_control_type); |
+ if (!xml_writer->AddAttribute(kAttributeControlType, |
+ field.form_control_type)) |
+ return false; |
if (!field.label.empty()) { |
std::string truncated; |
base::TruncateUTF8ToByteSize(base::UTF16ToUTF8(field.label), |
kMaxFieldLabelNumChars, &truncated); |
- field_element->SetAttr(buzz::QName(kAttributeFieldLabel), truncated); |
+ if (!xml_writer->AddAttribute(kAttributeFieldLabel, truncated)) |
+ return false; |
} |
} |
- parent->AddElement(field_element); |
- return field_element; |
+ return true; |
} |
-// Helper for |EncodeFormRequest()| that creates XmlElements for the given field |
-// in upload xml, and also add them to the parent XmlElement. |
-void EncodeFieldForUpload(const AutofillField& field, |
- buzz::XmlElement* parent) { |
+// Uses |xml_writer| to write one tag named |kXMLElementField| for each item in |
+// |field.possible_types()|, and adds some field attributes based on |field|. |
+// Returns true on success, false on failure. |
+bool EncodeFieldForUpload(const AutofillField& field, XmlWriter* xml_writer) { |
// Don't upload checkable fields. |
if (field.is_checkable) |
- return; |
+ return true; |
- ServerFieldTypeSet types = field.possible_types(); |
- // |types| could be empty in unit-tests only. |
+ const ServerFieldTypeSet& types = field.possible_types(); |
for (const auto& field_type : types) { |
- // We use the same field elements as the query and add a few more below. |
- buzz::XmlElement* field_element = EncodeFieldForQuery(field, parent); |
+ if (!xml_writer->StartElement(kXMLElementField)) |
+ return false; |
+ // Add the same field elements as the query and a few more below. |
+ if (!EncodeFieldForQuery(field, xml_writer)) |
+ return false; |
if (IsAutofillFieldMetadataEnabled() && |
!field.autocomplete_attribute.empty()) { |
- field_element->SetAttr(buzz::QName(kAttributeAutocomplete), |
- field.autocomplete_attribute); |
+ if (!xml_writer->AddAttribute(kAttributeAutocomplete, |
+ field.autocomplete_attribute)) |
+ return false; |
} |
- field_element->SetAttr(buzz::QName(kAttributeAutofillType), |
- base::IntToString(field_type)); |
+ if (!xml_writer->AddAttribute(kAttributeAutofillType, |
+ base::IntToString(field_type))) |
+ return false; |
+ if (!xml_writer->EndElement()) |
+ return false; |
} |
+ return true; |
} |
-// Helper for |EncodeFormRequest()| that creates XmlElements for the given field |
-// in field assignments xml, and also add them to the parent XmlElement. |
-void EncodeFieldForFieldAssignments(const AutofillField& field, |
- buzz::XmlElement* parent) { |
- ServerFieldTypeSet types = field.possible_types(); |
+// Uses |xml_writer| to write one tag named |kXMLElementFields| for each item in |
+// |field.possible_types()|, and adds some field attributes based on |field|. |
+// Returns true on success, false on failure. |
+bool EncodeFieldForFieldAssignments(const AutofillField& field, |
+ XmlWriter* xml_writer) { |
+ const ServerFieldTypeSet& types = field.possible_types(); |
for (const auto& field_type : types) { |
- buzz::XmlElement *field_element = new buzz::XmlElement( |
- buzz::QName(kXMLElementFields)); |
- |
- field_element->SetAttr(buzz::QName(kAttributeFieldID), |
- field.FieldSignature()); |
- field_element->SetAttr(buzz::QName(kAttributeFieldType), |
- base::IntToString(field_type)); |
- field_element->SetAttr(buzz::QName(kAttributeName), |
- base::UTF16ToUTF8(field.name)); |
- parent->AddElement(field_element); |
+ if (!xml_writer->StartElement(kXMLElementFields)) |
+ return false; |
+ |
+ if (!xml_writer->AddAttribute(kAttributeFieldID, field.FieldSignature())) |
+ return false; |
+ if (!xml_writer->AddAttribute(kAttributeFieldType, |
+ base::IntToString(field_type))) |
+ return false; |
+ if (!xml_writer->AddAttribute(kAttributeName, |
+ base::UTF16ToUTF8(field.name))) |
+ return false; |
+ if (!xml_writer->EndElement()) |
+ return false; |
} |
+ return true; |
} |
// Returns |true| iff the |token| is a type hint for a contact field, as |
@@ -442,49 +454,59 @@ bool FormStructure::EncodeUploadRequest( |
bool form_was_autofilled, |
const std::string& login_form_signature, |
std::string* encoded_xml) const { |
+ DCHECK(encoded_xml); |
DCHECK(ShouldBeCrowdsourced()); |
// Verify that |available_field_types| agrees with the possible field types we |
// are uploading. |
for (const AutofillField* field : *this) { |
for (const auto& type : field->possible_types()) { |
- DCHECK(type == UNKNOWN_TYPE || |
- type == EMPTY_TYPE || |
+ DCHECK(type == UNKNOWN_TYPE || type == EMPTY_TYPE || |
available_field_types.count(type)); |
} |
} |
// Set up the <autofillupload> element and its attributes. |
- buzz::XmlElement autofill_request_xml( |
- (buzz::QName(kXMLElementAutofillUpload))); |
- autofill_request_xml.SetAttr(buzz::QName(kAttributeClientVersion), |
- kClientVersion); |
- autofill_request_xml.SetAttr(buzz::QName(kAttributeFormSignature), |
- FormSignature()); |
- autofill_request_xml.SetAttr(buzz::QName(kAttributeAutofillUsed), |
- form_was_autofilled ? "true" : "false"); |
- autofill_request_xml.SetAttr(buzz::QName(kAttributeDataPresent), |
- EncodeFieldTypes(available_field_types).c_str()); |
+ XmlWriter xml_writer; |
+ xml_writer.StartWriting(); |
+ xml_writer.StopIndenting(); |
+ if (!xml_writer.StartElement(kXMLElementAutofillUpload)) |
+ return false; |
+ if (!xml_writer.AddAttribute(kAttributeClientVersion, kClientVersion)) |
+ return false; |
+ if (!xml_writer.AddAttribute(kAttributeFormSignature, FormSignature())) |
+ return false; |
+ if (!xml_writer.AddAttribute(kAttributeAutofillUsed, |
+ form_was_autofilled ? "true" : "false")) |
+ return false; |
+ if (!xml_writer.AddAttribute(kAttributeDataPresent, |
+ EncodeFieldTypes(available_field_types))) |
+ return false; |
if (IsAutofillFieldMetadataEnabled()) { |
- autofill_request_xml.SetAttr(buzz::QName(kAttributeFormActionHostSignature), |
- Hash64Bit(target_url_.host())); |
- if(!form_name().empty()) { |
- autofill_request_xml.SetAttr(buzz::QName(kAttributeFormName), |
- base::UTF16ToUTF8(form_name())); |
+ if (!xml_writer.AddAttribute(kAttributeFormActionHostSignature, |
+ Hash64Bit(target_url_.host()))) |
+ return false; |
+ if (!form_name().empty()) { |
+ if (!xml_writer.AddAttribute(kAttributeFormName, |
+ base::UTF16ToUTF8(form_name()))) |
+ return false; |
} |
} |
if (!login_form_signature.empty()) { |
- autofill_request_xml.SetAttr(buzz::QName(kAttributeLoginFormSignature), |
- login_form_signature); |
+ if (!xml_writer.AddAttribute(kAttributeLoginFormSignature, |
+ login_form_signature)) |
+ return false; |
} |
- if (!EncodeFormRequest(FormStructure::UPLOAD, &autofill_request_xml)) |
+ if (IsMalformed() || !EncodeFormRequest(FormStructure::UPLOAD, &xml_writer)) |
return false; // Malformed form, skip it. |
+ if (!xml_writer.EndElement()) |
+ return false; |
// Obtain the XML structure as a string. |
- *encoded_xml = kXMLDeclaration; |
- *encoded_xml += autofill_request_xml.Str().c_str(); |
+ xml_writer.StopWriting(); |
+ *encoded_xml = xml_writer.GetWrittenString(); |
// To enable this logging, run with the flag --vmodule="form_structure=2". |
VLOG(2) << "\n" << *encoded_xml; |
@@ -495,21 +517,28 @@ bool FormStructure::EncodeUploadRequest( |
bool FormStructure::EncodeFieldAssignments( |
const ServerFieldTypeSet& available_field_types, |
std::string* encoded_xml) const { |
+ DCHECK(encoded_xml); |
DCHECK(ShouldBeCrowdsourced()); |
// Set up the <fieldassignments> element and its attributes. |
- buzz::XmlElement autofill_request_xml( |
- (buzz::QName(kXMLElementFieldAssignments))); |
- autofill_request_xml.SetAttr(buzz::QName(kAttributeFormSignature), |
- FormSignature()); |
+ XmlWriter xml_writer; |
+ xml_writer.StartWriting(); |
+ xml_writer.StopIndenting(); |
+ if (!xml_writer.StartElement(kXMLElementFieldAssignments)) |
+ return false; |
+ if (!xml_writer.AddAttribute(kAttributeFormSignature, FormSignature())) |
+ return false; |
- if (!EncodeFormRequest(FormStructure::FIELD_ASSIGNMENTS, |
- &autofill_request_xml)) |
+ if (IsMalformed() || |
+ !EncodeFormRequest(FormStructure::FIELD_ASSIGNMENTS, &xml_writer)) { |
return false; // Malformed form, skip it. |
+ } |
+ if (!xml_writer.EndElement()) |
+ return false; |
// Obtain the XML structure as a string. |
- *encoded_xml = kXMLDeclaration; |
- *encoded_xml += autofill_request_xml.Str().c_str(); |
+ xml_writer.StopWriting(); |
+ *encoded_xml = xml_writer.GetWrittenString(); |
return true; |
} |
@@ -521,37 +550,43 @@ bool FormStructure::EncodeQueryRequest( |
std::string* encoded_xml) { |
DCHECK(encoded_signatures); |
DCHECK(encoded_xml); |
- encoded_xml->clear(); |
encoded_signatures->clear(); |
encoded_signatures->reserve(forms.size()); |
// Set up the <autofillquery> element and attributes. |
- buzz::XmlElement autofill_request_xml( |
- (buzz::QName(kXMLElementAutofillQuery))); |
- autofill_request_xml.SetAttr(buzz::QName(kAttributeClientVersion), |
- kClientVersion); |
+ XmlWriter xml_writer; |
+ xml_writer.StartWriting(); |
+ xml_writer.StopIndenting(); |
+ if (!xml_writer.StartElement(kXMLElementAutofillQuery)) |
+ return false; |
+ if (!xml_writer.AddAttribute(kAttributeClientVersion, kClientVersion)) |
+ return false; |
// Some badly formatted web sites repeat forms - detect that and encode only |
// one form as returned data would be the same for all the repeated forms. |
std::set<std::string> processed_forms; |
- for (const auto& it : forms) { |
- std::string signature(it->FormSignature()); |
+ for (const auto& form : forms) { |
+ std::string signature(form->FormSignature()); |
if (processed_forms.find(signature) != processed_forms.end()) |
continue; |
processed_forms.insert(signature); |
- scoped_ptr<buzz::XmlElement> encompassing_xml_element( |
- new buzz::XmlElement(buzz::QName(kXMLElementForm))); |
- encompassing_xml_element->SetAttr(buzz::QName(kAttributeSignature), |
- signature); |
- |
- if (!it->EncodeFormRequest(FormStructure::QUERY, |
- encompassing_xml_element.get())) { |
- continue; // Malformed form, skip it. |
- } |
+ if (form->IsMalformed()) |
+ continue; |
+ if (!xml_writer.StartElement(kXMLElementForm)) |
+ return false; |
+ if (!xml_writer.AddAttribute(kAttributeSignature, signature)) |
+ return false; |
+ |
+ if (!form->EncodeFormRequest(FormStructure::QUERY, &xml_writer)) |
+ continue; |
+ |
+ if (!xml_writer.EndElement()) |
+ return false; |
- autofill_request_xml.AddElement(encompassing_xml_element.release()); |
encoded_signatures->push_back(signature); |
} |
+ if (!xml_writer.EndElement()) |
+ return false; |
if (!encoded_signatures->size()) |
return false; |
@@ -561,14 +596,14 @@ bool FormStructure::EncodeQueryRequest( |
// it ever resurfaces, re-add code here to set the attribute accordingly. |
// Obtain the XML structure as a string. |
- *encoded_xml = kXMLDeclaration; |
- *encoded_xml += autofill_request_xml.Str().c_str(); |
+ xml_writer.StopWriting(); |
+ *encoded_xml = xml_writer.GetWrittenString(); |
return true; |
} |
// static |
-void FormStructure::ParseQueryResponse(const std::string& response_xml, |
+void FormStructure::ParseQueryResponse(std::string response_xml, |
const std::vector<FormStructure*>& forms, |
rappor::RapporService* rappor_service) { |
AutofillMetrics::LogServerQueryMetric( |
@@ -577,11 +612,7 @@ void FormStructure::ParseQueryResponse(const std::string& response_xml, |
// Parse the field types from the server response to the query. |
std::vector<AutofillServerFieldInfo> field_infos; |
UploadRequired upload_required; |
- AutofillQueryXmlParser parse_handler(&field_infos, |
- &upload_required); |
- buzz::XmlParser parser(&parse_handler); |
- parser.Parse(response_xml.c_str(), response_xml.length(), true); |
- if (!parse_handler.succeeded()) |
+ if (!ParseAutofillQueryXml(response_xml, &field_infos, &upload_required)) |
return; |
AutofillMetrics::LogServerQueryMetric(AutofillMetrics::QUERY_RESPONSE_PARSED); |
@@ -1027,11 +1058,9 @@ std::string FormStructure::Hash64Bit(const std::string& str) { |
return base::Uint64ToString(hash64); |
} |
-bool FormStructure::EncodeFormRequest( |
- FormStructure::EncodeRequestType request_type, |
- buzz::XmlElement* encompassing_xml_element) const { |
+bool FormStructure::IsMalformed() const { |
if (!field_count()) // Nothing to add. |
- return false; |
+ return true; |
// Some badly formatted web sites repeat fields - limit number of fields to |
// 48, which is far larger than any valid form and XML still fits into 2K. |
@@ -1039,22 +1068,33 @@ bool FormStructure::EncodeFormRequest( |
// near certainly not valid/auto-fillable. |
const size_t kMaxFieldsOnTheForm = 48; |
if (field_count() > kMaxFieldsOnTheForm) |
- return false; |
+ return true; |
+ return false; |
+} |
+bool FormStructure::EncodeFormRequest(EncodeRequestType request_type, |
+ XmlWriter* xml_writer) const { |
+ DCHECK(!IsMalformed()); |
// Add the child nodes for the form fields. |
- for (size_t index = 0; index < field_count(); ++index) { |
- const AutofillField* field = fields_[index]; |
+ for (const AutofillField* field : fields_) { |
switch (request_type) { |
case FormStructure::UPLOAD: |
- EncodeFieldForUpload(*field, encompassing_xml_element); |
+ if (!EncodeFieldForUpload(*field, xml_writer)) |
+ return false; |
break; |
case FormStructure::QUERY: |
if (ShouldSkipField(*field)) |
continue; |
- EncodeFieldForQuery(*field, encompassing_xml_element); |
+ if (!xml_writer->StartElement(kXMLElementField)) |
+ return false; |
+ if (!EncodeFieldForQuery(*field, xml_writer)) |
+ return false; |
+ if (!xml_writer->EndElement()) |
+ return false; |
break; |
case FormStructure::FIELD_ASSIGNMENTS: |
- EncodeFieldForFieldAssignments(*field, encompassing_xml_element); |
+ if (!EncodeFieldForFieldAssignments(*field, xml_writer)) |
+ return false; |
break; |
} |
} |