Extracting advance element details from Autofill server XML response.


BUG=


Review URL: https://chromiumcodereview.appspot.com/12051017

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@178625 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/autofill/autofill_xml_parser.cc b/chrome/browser/autofill/autofill_xml_parser.cc
index b2e4c04c..ea1a605 100644
--- a/chrome/browser/autofill/autofill_xml_parser.cc
+++ b/chrome/browser/autofill/autofill_xml_parser.cc
@@ -15,6 +15,8 @@
     : succeeded_(true) {
 }
 
+AutofillXmlParser::~AutofillXmlParser() {}
+
 void AutofillXmlParser::CharacterData(
     buzz::XmlParseContext* context, const char* text, int len) {
 }
@@ -41,6 +43,8 @@
   DCHECK(experiment_id_);
 }
 
+AutofillQueryXmlParser::~AutofillQueryXmlParser() {}
+
 void AutofillQueryXmlParser::StartElement(buzz::XmlParseContext* context,
                                           const char* name,
                                           const char** attrs) {
@@ -112,6 +116,32 @@
         total_pages_ = GetIntValue(context, *attrs);
       ++attrs;
     }
+  } else if (element.compare("page_advance_button") == 0) {
+    // |attrs| is a NULL-terminated list of (attribute, value) pairs.
+    // If both id and css_selector are set, the first one to appear will take
+    // precedence.
+    while (*attrs) {
+      buzz::QName attribute_qname = context->ResolveQName(*attrs, true);
+      ++attrs;
+      const std::string& attribute_name = attribute_qname.LocalPart();
+      buzz::QName value_qname = context->ResolveQName(*attrs, true);
+      ++attrs;
+      const std::string& attribute_value = value_qname.LocalPart();
+      if (attribute_name.compare("id") == 0 && !attribute_value.empty()) {
+        proceed_element_descriptor_.reset(new autofill::WebElementDescriptor());
+        proceed_element_descriptor_->retrieval_method =
+            autofill::WebElementDescriptor::ID;
+        proceed_element_descriptor_->descriptor = attribute_value;
+        break;
+      } else if (attribute_name.compare("css_selector") == 0 &&
+                 !attribute_value.empty()) {
+        proceed_element_descriptor_.reset(new autofill::WebElementDescriptor());
+        proceed_element_descriptor_->retrieval_method =
+            autofill::WebElementDescriptor::CSS_SELECTOR;
+        proceed_element_descriptor_->descriptor = attribute_value;
+        break;
+      }
+    }
   }
 }
 
diff --git a/chrome/browser/autofill/autofill_xml_parser.h b/chrome/browser/autofill/autofill_xml_parser.h
index 484478c6..efa8c0e 100644
--- a/chrome/browser/autofill/autofill_xml_parser.h
+++ b/chrome/browser/autofill/autofill_xml_parser.h
@@ -10,9 +10,11 @@
 
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
 #include "chrome/browser/autofill/autofill_server_field_info.h"
 #include "chrome/browser/autofill/field_types.h"
 #include "chrome/browser/autofill/form_structure.h"
+#include "chrome/common/autofill/web_element_descriptor.h"
 #include "third_party/libjingle/source/talk/xmllite/xmlparser.h"
 
 // The base class that contains common functionality between
@@ -20,6 +22,7 @@
 class AutofillXmlParser : public buzz::XmlParseHandler {
  public:
   AutofillXmlParser();
+  virtual ~AutofillXmlParser();
 
   // Returns true if no parsing errors were encountered.
   bool succeeded() const { return succeeded_; }
@@ -70,11 +73,18 @@
   AutofillQueryXmlParser(std::vector<AutofillServerFieldInfo>* field_infos,
                          UploadRequired* upload_required,
                          std::string* experiment_id);
+  virtual ~AutofillQueryXmlParser();
 
   int current_page_number() const { return current_page_number_; }
 
   int total_pages() const { return total_pages_; }
 
+  // Returns the proceed element for multipage Autofill flows if the current
+  // page is part of such a flow or NULL otherwise.
+  const autofill::WebElementDescriptor* proceed_element_descriptor() const {
+    return proceed_element_descriptor_.get();
+  }
+
  private:
   // A callback for the beginning of a new <element>, called by Expat.
   // |context| is a parsing context used to resolve element/attribute names.
@@ -103,6 +113,9 @@
   // Total number of pages in multipage autofill flow.
   int total_pages_;
 
+  // Proceed element for multipage Autofill flow.
+  scoped_ptr<autofill::WebElementDescriptor> proceed_element_descriptor_;
+
   // The server experiment to which this query response belongs.
   // For the default server implementation, this is empty.
   std::string* experiment_id_;
diff --git a/chrome/browser/autofill/autofill_xml_parser_unittest.cc b/chrome/browser/autofill/autofill_xml_parser_unittest.cc
index 616fa9be..19cca97 100644
--- a/chrome/browser/autofill/autofill_xml_parser_unittest.cc
+++ b/chrome/browser/autofill/autofill_xml_parser_unittest.cc
@@ -168,7 +168,9 @@
 
   std::string xml = "<autofillqueryresponse>"
                     "<field autofilltype=\"55\"/>"
-                    "<autofill_flow page_no=\"1\" total_pages=\"10\"/>"
+                    "<autofill_flow page_no=\"1\" total_pages=\"10\">"
+                    "<page_advance_button id=\"foo\"/>"
+                    "</autofill_flow>"
                     "</autofillqueryresponse>";
 
   scoped_ptr<AutofillQueryXmlParser> parse_handler(
@@ -180,6 +182,60 @@
   EXPECT_EQ(1U, field_infos.size());
   EXPECT_EQ(1, parse_handler->current_page_number());
   EXPECT_EQ(10, parse_handler->total_pages());
+  EXPECT_EQ("foo", parse_handler->proceed_element_descriptor()->descriptor);
+  EXPECT_EQ(autofill::WebElementDescriptor::ID,
+            parse_handler->proceed_element_descriptor()->retrieval_method);
+
+  // Clear |field_infos| for the next test;
+  field_infos.clear();
+
+  // Test css_selector as page_advance_button.
+  xml = "<autofillqueryresponse>"
+        "<field autofilltype=\"55\"/>"
+        "<autofill_flow page_no=\"1\" total_pages=\"10\">"
+        "<page_advance_button css_selector=\"[name=&quot;foo&quot;]\"/>"
+        "</autofill_flow>"
+        "</autofillqueryresponse>";
+
+  parse_handler.reset(new AutofillQueryXmlParser(&field_infos,
+                                                 &upload_required,
+                                                 &experiment_id));
+  parser.reset(new buzz::XmlParser(parse_handler.get()));
+  parser->Parse(xml.c_str(), xml.length(), true);
+  EXPECT_TRUE(parse_handler->succeeded());
+  EXPECT_EQ(1U, field_infos.size());
+  EXPECT_EQ(1, parse_handler->current_page_number());
+  EXPECT_EQ(10, parse_handler->total_pages());
+  EXPECT_EQ("[name=\"foo\"]",
+            parse_handler->proceed_element_descriptor()->descriptor);
+  EXPECT_EQ(autofill::WebElementDescriptor::CSS_SELECTOR,
+            parse_handler->proceed_element_descriptor()->retrieval_method);
+
+  // Clear |field_infos| for the next test;
+  field_infos.clear();
+
+  // Test first attribute is always the one set.
+  xml = "<autofillqueryresponse>"
+        "<field autofilltype=\"55\"/>"
+        "<autofill_flow page_no=\"1\" total_pages=\"10\">"
+        "<page_advance_button css_selector=\"[name=&quot;foo&quot;]\""
+        "   id=\"foo\"/>"
+        "</autofill_flow>"
+        "</autofillqueryresponse>";
+
+  parse_handler.reset(new AutofillQueryXmlParser(&field_infos,
+                                                 &upload_required,
+                                                 &experiment_id));
+  parser.reset(new buzz::XmlParser(parse_handler.get()));
+  parser->Parse(xml.c_str(), xml.length(), true);
+  EXPECT_TRUE(parse_handler->succeeded());
+  EXPECT_EQ(1U, field_infos.size());
+  EXPECT_EQ(1, parse_handler->current_page_number());
+  EXPECT_EQ(10, parse_handler->total_pages());
+  EXPECT_EQ("[name=\"foo\"]",
+            parse_handler->proceed_element_descriptor()->descriptor);
+  EXPECT_EQ(autofill::WebElementDescriptor::CSS_SELECTOR,
+            parse_handler->proceed_element_descriptor()->retrieval_method);
 }
 
 // Test badly formed XML queries.
diff --git a/chrome/browser/autofill/form_structure.cc b/chrome/browser/autofill/form_structure.cc
index 7c89eb8..28d3535c9 100644
--- a/chrome/browser/autofill/form_structure.cc
+++ b/chrome/browser/autofill/form_structure.cc
@@ -446,6 +446,11 @@
     form->server_experiment_id_ = experiment_id;
     form->current_page_number_ = parse_handler.current_page_number();
     form->total_pages_ = parse_handler.total_pages();
+    if (parse_handler.proceed_element_descriptor()) {
+      form->proceed_element_descriptor_.reset(
+          new autofill::WebElementDescriptor(
+              *parse_handler.proceed_element_descriptor()));
+    }
 
     for (std::vector<AutofillField*>::iterator field = form->fields_.begin();
          field != form->fields_.end(); ++field, ++current_info) {
diff --git a/chrome/browser/autofill/form_structure.h b/chrome/browser/autofill/form_structure.h
index 118dc5d..72485af 100644
--- a/chrome/browser/autofill/form_structure.h
+++ b/chrome/browser/autofill/form_structure.h
@@ -9,10 +9,12 @@
 #include <vector>
 
 #include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/memory/scoped_vector.h"
 #include "chrome/browser/autofill/autofill_field.h"
 #include "chrome/browser/autofill/autofill_type.h"
 #include "chrome/browser/autofill/field_types.h"
+#include "chrome/common/autofill/web_element_descriptor.h"
 #include "googleurl/src/gurl.h"
 
 struct FormData;
@@ -132,6 +134,12 @@
   // autofillable flow.
   bool IsInAutofillableFlow() const;
 
+  // Returns the proceed element for multipage Autofill flows if the current
+  // page is part of such a flow or NULL otherwise.
+  const autofill::WebElementDescriptor* proceed_element_descriptor() const {
+    return proceed_element_descriptor_.get();
+  }
+
   const AutofillField* field(size_t index) const;
   AutofillField* field(size_t index);
   size_t field_count() const;
@@ -228,6 +236,9 @@
   // to any autofill flow, it is set to -1.
   int total_pages_;
 
+  // Proceed element for multipage Autofill flow.
+  scoped_ptr<autofill::WebElementDescriptor> proceed_element_descriptor_;
+
   // Whether the form includes any field types explicitly specified by the site
   // author, via the |autocompletetype| attribute.
   bool has_author_specified_types_;