autofill_xml_parser.cc revision a36e5920737c6adbddd3e43b760e5de8431db6e0
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/autofill/core/browser/autofill_xml_parser.h"
6
7#include <stdlib.h>
8#include <string.h>
9
10#include "base/logging.h"
11#include "base/strings/string_number_conversions.h"
12#include "components/autofill/content/browser/autocheckout_page_meta_data.h"
13#include "components/autofill/core/browser/autofill_server_field_info.h"
14#include "third_party/libjingle/source/talk/xmllite/qname.h"
15
16namespace autofill {
17
18AutofillXmlParser::AutofillXmlParser()
19    : succeeded_(true) {
20}
21
22AutofillXmlParser::~AutofillXmlParser() {}
23
24void AutofillXmlParser::CharacterData(
25    buzz::XmlParseContext* context, const char* text, int len) {
26}
27
28void AutofillXmlParser::EndElement(buzz::XmlParseContext* context,
29                                   const char* name) {
30}
31
32void AutofillXmlParser::Error(buzz::XmlParseContext* context,
33                              XML_Error error_code) {
34  succeeded_ = false;
35}
36
37AutofillQueryXmlParser::AutofillQueryXmlParser(
38    std::vector<AutofillServerFieldInfo>* field_infos,
39    UploadRequired* upload_required,
40    std::string* experiment_id,
41    AutocheckoutPageMetaData* page_meta_data)
42    : field_infos_(field_infos),
43      upload_required_(upload_required),
44      experiment_id_(experiment_id),
45      page_meta_data_(page_meta_data),
46      current_click_element_(NULL),
47      current_page_number_for_page_types_(0),
48      is_in_type_section_(false) {
49  DCHECK(upload_required_);
50  DCHECK(experiment_id_);
51  DCHECK(page_meta_data_);
52}
53
54AutofillQueryXmlParser::~AutofillQueryXmlParser() {}
55
56void AutofillQueryXmlParser::StartElement(buzz::XmlParseContext* context,
57                                          const char* name,
58                                          const char** attrs) {
59  buzz::QName qname = context->ResolveQName(name, false);
60  const std::string& element = qname.LocalPart();
61  if (element.compare("autofillqueryresponse") == 0) {
62    // We check for the upload required attribute below, but if it's not
63    // present, we use the default upload rates. Likewise, by default we assume
64    // an empty experiment id.
65    *upload_required_ = USE_UPLOAD_RATES;
66    *experiment_id_ = std::string();
67
68    // |attrs| is a NULL-terminated list of (attribute, value) pairs.
69    while (*attrs) {
70      buzz::QName attribute_qname = context->ResolveQName(*attrs, true);
71      ++attrs;
72      const std::string& attribute_name = attribute_qname.LocalPart();
73      if (attribute_name.compare("uploadrequired") == 0) {
74        if (strcmp(*attrs, "true") == 0)
75          *upload_required_ = UPLOAD_REQUIRED;
76        else if (strcmp(*attrs, "false") == 0)
77          *upload_required_ = UPLOAD_NOT_REQUIRED;
78      } else if (attribute_name.compare("experimentid") == 0) {
79        *experiment_id_ = *attrs;
80      }
81      ++attrs;
82    }
83  } else if (element.compare("field") == 0) {
84    if (!*attrs) {
85      // Missing the "autofilltype" attribute, abort.
86      context->RaiseError(XML_ERROR_ABORTED);
87      return;
88    }
89
90    // Determine the field type from the attribute value.  There should be one
91    // attribute (autofilltype) with an integer value.
92    AutofillServerFieldInfo field_info;
93    field_info.field_type = UNKNOWN_TYPE;
94
95    // |attrs| is a NULL-terminated list of (attribute, value) pairs.
96    while (*attrs) {
97      buzz::QName attribute_qname = context->ResolveQName(*attrs, true);
98      ++attrs;
99      const std::string& attribute_name = attribute_qname.LocalPart();
100      if (attribute_name.compare("autofilltype") == 0) {
101        int value = GetIntValue(context, *attrs);
102        if (value >= 0 && value < MAX_VALID_FIELD_TYPE)
103          field_info.field_type = static_cast<AutofillFieldType>(value);
104        else
105          field_info.field_type = NO_SERVER_DATA;
106      } else if (field_info.field_type == FIELD_WITH_DEFAULT_VALUE &&
107                 attribute_name.compare("defaultvalue") == 0) {
108        field_info.default_value = *attrs;
109      }
110      ++attrs;
111    }
112
113    // Record this field type, default value pair.
114    field_infos_->push_back(field_info);
115  } else if (element.compare("autofill_flow") == 0) {
116    // |attrs| is a NULL-terminated list of (attribute, value) pairs.
117    while (*attrs) {
118      buzz::QName attribute_qname = context->ResolveQName(*attrs, true);
119      ++attrs;
120      const std::string& attribute_name = attribute_qname.LocalPart();
121      if (attribute_name.compare("page_no") == 0)
122        page_meta_data_->current_page_number = GetIntValue(context, *attrs);
123      else if (attribute_name.compare("total_pages") == 0)
124        page_meta_data_->total_pages = GetIntValue(context, *attrs);
125      else if (attribute_name.compare("ignore_ajax") == 0)
126        page_meta_data_->ignore_ajax = strcmp(*attrs, "false") != 0;
127      ++attrs;
128    }
129  } else if (element.compare("page_advance_button") == 0) {
130    page_meta_data_->proceed_element_descriptor = WebElementDescriptor();
131    ParseElementDescriptor(context,
132                           attrs,
133                           &page_meta_data_->proceed_element_descriptor);
134  } else if (element.compare("click_elements_before_formfill") == 0) {
135    page_meta_data_->click_elements_before_form_fill.push_back(
136        WebElementDescriptor());
137    current_click_element_ = &page_meta_data_->click_elements_before_form_fill.
138        back();
139  } else if (element.compare("click_elements_after_formfill") == 0) {
140    page_meta_data_->click_elements_after_form_fill.push_back(
141        WebElementDescriptor());
142    current_click_element_ = &page_meta_data_->click_elements_after_form_fill.
143        back();
144  } else if (element.compare("web_element") == 0) {
145    ParseElementDescriptor(context, attrs, current_click_element_);
146  } else if (element.compare("flow_page") == 0) {
147    while (*attrs) {
148      buzz::QName attribute_qname = context->ResolveQName(*attrs, true);
149      ++attrs;
150      const std::string& attribute_name = attribute_qname.LocalPart();
151      if (attribute_name.compare("page_no") == 0)
152        current_page_number_for_page_types_ = GetIntValue(context, *attrs);
153      ++attrs;
154    }
155  } else if (element.compare("type") == 0) {
156    is_in_type_section_ = true;
157  }
158}
159
160void AutofillQueryXmlParser::ParseElementDescriptor(
161    buzz::XmlParseContext* context,
162    const char* const* attrs,
163    WebElementDescriptor* element_descriptor) {
164  // If both id and css_selector are set, the first one to appear will take
165  // precedence.
166  // |attrs| is a NULL-terminated list of (attribute, value) pairs.
167  while (*attrs) {
168    buzz::QName attribute_qname = context->ResolveQName(*attrs, true);
169    ++attrs;
170    const std::string& attribute_name = attribute_qname.LocalPart();
171    buzz::QName value_qname = context->ResolveQName(*attrs, true);
172    ++attrs;
173    const std::string& attribute_value = value_qname.LocalPart();
174    if (attribute_name.compare("id") == 0 && !attribute_value.empty()) {
175      element_descriptor->retrieval_method = autofill::WebElementDescriptor::ID;
176      element_descriptor->descriptor = attribute_value;
177      break;
178    } else if (attribute_name.compare("css_selector") == 0 &&
179               !attribute_value.empty()) {
180      element_descriptor->retrieval_method =
181          autofill::WebElementDescriptor::CSS_SELECTOR;
182      element_descriptor->descriptor = attribute_value;
183      break;
184    }
185  }
186}
187
188void AutofillQueryXmlParser::EndElement(buzz::XmlParseContext* context,
189                                   const char* name) {
190  is_in_type_section_ = false;
191}
192
193void AutofillQueryXmlParser::CharacterData(
194    buzz::XmlParseContext* context, const char* text, int len) {
195  if (!is_in_type_section_)
196    return;
197
198  int type = -1;
199  base::StringToInt(std::string(text, len), &type);
200  if (type >= AUTOCHECKOUT_STEP_MIN_VALUE &&
201      type <= AUTOCHECKOUT_STEP_MAX_VALUE) {
202    AutocheckoutStepType step_type =
203        static_cast<AutocheckoutStepType>(type);
204    page_meta_data_->page_types[current_page_number_for_page_types_]
205        .push_back(step_type);
206  }
207}
208
209int AutofillQueryXmlParser::GetIntValue(buzz::XmlParseContext* context,
210                                        const char* attribute) {
211  char* attr_end = NULL;
212  int value = strtol(attribute, &attr_end, 10);
213  if (attr_end != NULL && attr_end == attribute) {
214    context->RaiseError(XML_ERROR_SYNTAX);
215    return 0;
216  }
217  return value;
218}
219
220AutofillUploadXmlParser::AutofillUploadXmlParser(double* positive_upload_rate,
221                                                 double* negative_upload_rate)
222    : succeeded_(false),
223      positive_upload_rate_(positive_upload_rate),
224      negative_upload_rate_(negative_upload_rate) {
225  DCHECK(positive_upload_rate_);
226  DCHECK(negative_upload_rate_);
227}
228
229void AutofillUploadXmlParser::StartElement(buzz::XmlParseContext* context,
230                                           const char* name,
231                                           const char** attrs) {
232  buzz::QName qname = context->ResolveQName(name, false);
233  const std::string &element = qname.LocalPart();
234  if (element.compare("autofilluploadresponse") == 0) {
235    // Loop over all attributes to get the upload rates.
236    while (*attrs) {
237      buzz::QName attribute_qname = context->ResolveQName(attrs[0], true);
238      const std::string &attribute_name = attribute_qname.LocalPart();
239      if (attribute_name.compare("positiveuploadrate") == 0) {
240        *positive_upload_rate_ = GetDoubleValue(context, attrs[1]);
241      } else if (attribute_name.compare("negativeuploadrate") == 0) {
242        *negative_upload_rate_ = GetDoubleValue(context, attrs[1]);
243      }
244      attrs += 2;  // We peeked at attrs[0] and attrs[1], skip past both.
245    }
246  }
247}
248
249double AutofillUploadXmlParser::GetDoubleValue(buzz::XmlParseContext* context,
250                                               const char* attribute) {
251  char* attr_end = NULL;
252  double value = strtod(attribute, &attr_end);
253  if (attr_end != NULL && attr_end == attribute) {
254    context->RaiseError(XML_ERROR_SYNTAX);
255    return 0.0;
256  }
257  return value;
258}
259
260}  // namespace autofill
261