summaryrefslogtreecommitdiffstats
path: root/chrome/browser/autofill
diff options
context:
space:
mode:
authorgeorgey@chromium.org <georgey@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-02 22:01:20 +0000
committergeorgey@chromium.org <georgey@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-02 22:01:20 +0000
commitdb16347063a2217641ddd992f1f029af24362a59 (patch)
tree9e74d716e342052d9c1989f13e110935d9a98763 /chrome/browser/autofill
parent4af869aded61930f3085f28548a89daf4ab2ef0f (diff)
downloadchromium_src-db16347063a2217641ddd992f1f029af24362a59.zip
chromium_src-db16347063a2217641ddd992f1f029af24362a59.tar.gz
chromium_src-db16347063a2217641ddd992f1f029af24362a59.tar.bz2
Behaving nice with AutoFill servers: Adjusting upload rate, processing 500, 502 and 503 responses, etc.
TEST=Unit-tested + by setting up the response from AutoFill server. BUG=39921 Review URL: http://codereview.chromium.org/1535011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@43531 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/autofill')
-rw-r--r--chrome/browser/autofill/autofill_download.cc124
-rw-r--r--chrome/browser/autofill/autofill_download.h41
-rw-r--r--chrome/browser/autofill/autofill_download_unittest.cc61
-rw-r--r--chrome/browser/autofill/autofill_manager.cc17
-rw-r--r--chrome/browser/autofill/autofill_xml_parser.h2
-rw-r--r--chrome/browser/autofill/autofill_xml_parser_unittest.cc55
6 files changed, 263 insertions, 37 deletions
diff --git a/chrome/browser/autofill/autofill_download.cc b/chrome/browser/autofill/autofill_download.cc
index d0ac8368..42d4a32 100644
--- a/chrome/browser/autofill/autofill_download.cc
+++ b/chrome/browser/autofill/autofill_download.cc
@@ -9,7 +9,11 @@
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/stl_util-inl.h"
+#include "chrome/browser/autofill/autofill_xml_parser.h"
+#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
+#include "chrome/common/pref_names.h"
+#include "net/http/http_response_headers.h"
#define DISABLED_REQUEST_URL "http://disabled"
@@ -18,21 +22,26 @@
#else
#define AUTO_FILL_QUERY_SERVER_REQUEST_URL DISABLED_REQUEST_URL
#define AUTO_FILL_UPLOAD_SERVER_REQUEST_URL DISABLED_REQUEST_URL
+#define AUTO_FILL_QUERY_SERVER_NAME_START_IN_HEADER "SOMESERVER/"
#endif
-namespace {
-// We only send a fraction of the forms to upload server.
-// The rate for positive/negative matches potentially could be different.
-const double kAutoFillPositiveUploadRate = 0.01;
-const double kAutoFillNegativeUploadRate = 0.01;
-} // namespace
-
-AutoFillDownloadManager::AutoFillDownloadManager()
- : observer_(NULL),
- positive_upload_rate_(kAutoFillPositiveUploadRate),
- negative_upload_rate_(kAutoFillNegativeUploadRate),
+AutoFillDownloadManager::AutoFillDownloadManager(Profile* profile)
+ : profile_(profile),
+ observer_(NULL),
+ next_query_request_(base::Time::Now()),
+ next_upload_request_(base::Time::Now()),
+ positive_upload_rate_(0),
+ negative_upload_rate_(0),
fetcher_id_for_unittest_(0),
is_testing_(false) {
+ // |profile_| could be NULL in some unit-tests.
+ if (profile_) {
+ PrefService* preferences = profile_->GetPrefs();
+ positive_upload_rate_ =
+ preferences->GetReal(prefs::kAutoFillPositiveUploadRate);
+ negative_upload_rate_ =
+ preferences->GetReal(prefs::kAutoFillNegativeUploadRate);
+ }
}
AutoFillDownloadManager::~AutoFillDownloadManager() {
@@ -52,6 +61,10 @@ void AutoFillDownloadManager::SetObserver(
bool AutoFillDownloadManager::StartQueryRequest(
const ScopedVector<FormStructure>& forms) {
+ if (next_query_request_ > base::Time::Now()) {
+ // We are in back-off mode: do not do the request.
+ return false;
+ }
std::string form_xml;
FormStructure::EncodeQueryRequest(forms, &form_xml);
@@ -70,15 +83,18 @@ bool AutoFillDownloadManager::StartQueryRequest(
bool AutoFillDownloadManager::StartUploadRequest(
const FormStructure& form, bool form_was_matched) {
+ if (next_upload_request_ > base::Time::Now()) {
+ // We are in back-off mode: do not do the request.
+ return false;
+ }
+
// Check if we need to upload form.
- // TODO(georgey): adjust this values from returned XML.
- double upload_rate = form_was_matched ? positive_upload_rate_ :
- negative_upload_rate_;
+ double upload_rate = form_was_matched ? GetPositiveUploadRate() :
+ GetNegativeUploadRate();
if (base::RandDouble() > upload_rate) {
LOG(INFO) << "AutoFillDownloadManager: Upload request is ignored";
- if (observer_)
- observer_->OnUploadedAutoFillHeuristics(form.FormSignature());
- return true;
+ // If we ever need notification that upload was skipped, add it here.
+ return false;
}
std::string form_xml;
form.EncodeUploadRequest(form_was_matched, &form_xml);
@@ -109,6 +125,35 @@ bool AutoFillDownloadManager::CancelRequest(
return false;
}
+double AutoFillDownloadManager::GetPositiveUploadRate() const {
+ return positive_upload_rate_;
+}
+
+double AutoFillDownloadManager::GetNegativeUploadRate() const {
+ return negative_upload_rate_;
+}
+
+void AutoFillDownloadManager::SetPositiveUploadRate(double rate) {
+ if (rate == positive_upload_rate_)
+ return;
+ positive_upload_rate_ = rate;
+ DCHECK_GE(rate, 0.0);
+ DCHECK_LE(rate, 1.0);
+ DCHECK(profile_);
+ PrefService* preferences = profile_->GetPrefs();
+ preferences->SetReal(prefs::kAutoFillPositiveUploadRate, rate);
+}
+
+void AutoFillDownloadManager::SetNegativeUploadRate(double rate) {
+ if (rate == negative_upload_rate_)
+ return;
+ negative_upload_rate_ = rate;
+ DCHECK_GE(rate, 0.0);
+ DCHECK_LE(rate, 1.0);
+ DCHECK(profile_);
+ PrefService* preferences = profile_->GetPrefs();
+ preferences->SetReal(prefs::kAutoFillNegativeUploadRate, rate);
+}
bool AutoFillDownloadManager::StartRequest(
const std::string& form_xml,
@@ -131,6 +176,7 @@ bool AutoFillDownloadManager::StartRequest(
URLFetcher::POST,
this);
url_fetchers_[fetcher] = request_data;
+ fetcher->set_automatcally_retry_on_5xx(false);
fetcher->set_request_context(Profile::GetDefaultRequestContext());
fetcher->set_upload_data("text/plain", form_xml);
fetcher->Start();
@@ -150,8 +196,39 @@ void AutoFillDownloadManager::OnURLFetchComplete(const URLFetcher* source,
it->second.request_type == AutoFillDownloadManager::REQUEST_QUERY ?
"query" : "upload");
const int kHttpResponseOk = 200;
+ const int kHttpInternalServerError = 500;
+ const int kHttpBadGateway = 502;
+ const int kHttpServiceUnavailable = 503;
+
DCHECK(it->second.form_signatures.size());
if (response_code != kHttpResponseOk) {
+ bool back_off = false;
+ std::string server_header;
+ switch (response_code) {
+ case kHttpBadGateway:
+ if (!source->response_headers()->EnumerateHeader(NULL, "server",
+ &server_header) ||
+ StartsWithASCII(server_header.c_str(),
+ AUTO_FILL_QUERY_SERVER_NAME_START_IN_HEADER,
+ false) != 0)
+ break;
+ // Bad getaway was received from AutoFill servers. Fall through to back
+ // off.
+ case kHttpInternalServerError:
+ case kHttpServiceUnavailable:
+ back_off = true;
+ break;
+ }
+
+ if (back_off) {
+ base::Time back_off_time(base::Time::Now() + source->backoff_delay());
+ if (it->second.request_type == AutoFillDownloadManager::REQUEST_QUERY) {
+ next_query_request_ = back_off_time;
+ } else {
+ next_upload_request_ = back_off_time;
+ }
+ }
+
LOG(INFO) << "AutoFillDownloadManager: " << type_of_request <<
" request has failed with response" << response_code;
if (observer_) {
@@ -166,7 +243,17 @@ void AutoFillDownloadManager::OnURLFetchComplete(const URLFetcher* source,
if (observer_)
observer_->OnLoadedAutoFillHeuristics(it->second.form_signatures, data);
} else {
- // TODO(georgey): adjust upload probabilities.
+ double new_positive_upload_rate = 0;
+ double new_negative_upload_rate = 0;
+ AutoFillUploadXmlParser parse_handler(&new_positive_upload_rate,
+ &new_negative_upload_rate);
+ buzz::XmlParser parser(&parse_handler);
+ parser.Parse(data.data(), data.length(), true);
+ if (parse_handler.succeeded()) {
+ SetPositiveUploadRate(new_positive_upload_rate);
+ SetNegativeUploadRate(new_negative_upload_rate);
+ }
+
if (observer_)
observer_->OnUploadedAutoFillHeuristics(it->second.form_signatures[0]);
}
@@ -175,4 +262,3 @@ void AutoFillDownloadManager::OnURLFetchComplete(const URLFetcher* source,
url_fetchers_.erase(it);
}
-
diff --git a/chrome/browser/autofill/autofill_download.h b/chrome/browser/autofill/autofill_download.h
index 620ac6f..a3511dd 100644
--- a/chrome/browser/autofill/autofill_download.h
+++ b/chrome/browser/autofill/autofill_download.h
@@ -12,11 +12,14 @@
#include "base/scoped_ptr.h"
#include "base/scoped_vector.h"
#include "base/string16.h"
+#include "base/time.h"
#include "chrome/browser/autofill/autofill_profile.h"
#include "chrome/browser/autofill/field_types.h"
#include "chrome/browser/autofill/form_structure.h"
#include "chrome/browser/net/url_fetcher.h"
+class Profile;
+
// Handles getting and updating AutoFill heuristics.
class AutoFillDownloadManager : public URLFetcher::Delegate {
public:
@@ -25,6 +28,7 @@ class AutoFillDownloadManager : public URLFetcher::Delegate {
REQUEST_UPLOAD,
};
// An interface used to notify clients of AutoFillDownloadManager.
+ // Notifications are *not* guaranteed to be called.
class Observer {
public:
// Called when heuristic successfully received from server.
@@ -49,7 +53,8 @@ class AutoFillDownloadManager : public URLFetcher::Delegate {
virtual ~Observer() {}
};
- AutoFillDownloadManager();
+ // |profile| can be NULL in unit-tests only.
+ explicit AutoFillDownloadManager(Profile* profile);
virtual ~AutoFillDownloadManager();
// |observer| - observer to notify on successful completion or error.
@@ -61,8 +66,8 @@ class AutoFillDownloadManager : public URLFetcher::Delegate {
bool StartQueryRequest(const ScopedVector<FormStructure>& forms);
// Start upload request if necessary. The probability of request going
- // over the wire are |positive_upload_rate_| if it was matched by
- // AutoFill, |negative_download_rate_| otherwise. Observer will be called
+ // over the wire are GetPositiveUploadRate() if it was matched by
+ // AutoFill, GetNegativeUploadRate() otherwise. Observer will be called
// even if there was no actual trip over the wire.
// |form| - form sent in this request.
// |form_was_matched| - true if form was matched by the AutoFill.
@@ -76,15 +81,17 @@ class AutoFillDownloadManager : public URLFetcher::Delegate {
bool CancelRequest(const std::string& form_signature,
AutoFillRequestType request_type);
- void SetPositiveUploadRate(double rate) {
- DCHECK(rate >= 0.0 && rate <= 1.0);
- positive_upload_rate_ = rate;
- }
-
- void SetNegativeUploadRate(double rate) {
- DCHECK(rate >= 0.0 && rate <= 1.0);
- negative_upload_rate_ = rate;
- }
+ // Probability of the form upload. Between 0 (no upload) and 1 (upload all).
+ // GetPositiveUploadRate() is for matched forms,
+ // GetNegativeUploadRate() for non matched.
+ double GetPositiveUploadRate() const;
+ double GetNegativeUploadRate() const;
+ // These functions called very rarely outside of theunit-tests. With current
+ // percentages, they would be called once per 100 auto-fillable forms filled
+ // and submitted by user. The order of magnitude would remain similar in the
+ // future.
+ void SetPositiveUploadRate(double rate);
+ void SetNegativeUploadRate(double rate);
private:
friend class AutoFillDownloadTestHelper; // unit-test.
@@ -110,13 +117,21 @@ class AutoFillDownloadManager : public URLFetcher::Delegate {
const ResponseCookies& cookies,
const std::string& data);
+ // Profile for preference storage.
+ Profile* profile_;
+
// For each requested form for both query and upload we create a separate
// request and save its info. As url fetcher is identified by its address
// we use a map between fetchers and info.
std::map<URLFetcher*, FormRequestData> url_fetchers_;
AutoFillDownloadManager::Observer *observer_;
- // Probability of the form upload. Between 0 (no upload) and 1 (upload all).
+ // Time when next query/upload requests are allowed. If 50x HTTP received,
+ // exponential back off is initiated, so this times will be in the future
+ // for awhile.
+ base::Time next_query_request_;
+ base::Time next_upload_request_;
+
// |positive_upload_rate_| is for matched forms,
// |negative_upload_rate_| for non matched.
double positive_upload_rate_;
diff --git a/chrome/browser/autofill/autofill_download_unittest.cc b/chrome/browser/autofill/autofill_download_unittest.cc
index dcf692c..e1df3b8 100644
--- a/chrome/browser/autofill/autofill_download_unittest.cc
+++ b/chrome/browser/autofill/autofill_download_unittest.cc
@@ -7,6 +7,7 @@
#include "base/string_util.h"
#include "chrome/browser/autofill/autofill_download.h"
#include "chrome/browser/net/test_url_fetcher_factory.h"
+#include "chrome/test/testing_profile.h"
#include "net/url_request/url_request_status.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/WebKit/chromium/public/WebInputElement.h"
@@ -24,7 +25,8 @@ using WebKit::WebInputElement;
// successful upload request, failed upload request.
class AutoFillDownloadTestHelper : public AutoFillDownloadManager::Observer {
public:
- AutoFillDownloadTestHelper() {
+ AutoFillDownloadTestHelper()
+ : download_manager(&profile) {
download_manager.SetObserver(this);
// For chromium builds forces Start*Request to actually execute.
download_manager.is_testing_ = true;
@@ -84,6 +86,7 @@ class AutoFillDownloadTestHelper : public AutoFillDownloadManager::Observer {
};
std::list<AutoFillDownloadTestHelper::ResponseData> responses_;
+ TestingProfile profile;
AutoFillDownloadManager download_manager;
};
@@ -175,6 +178,10 @@ TEST(AutoFillDownloadTest, QueryAndUploadTest) {
fetcher->delegate()->OnURLFetchComplete(fetcher, GURL(), URLRequestStatus(),
200, ResponseCookies(),
std::string(responses[1]));
+ // After that upload rates would be adjusted to 0.5/0.3
+ EXPECT_DOUBLE_EQ(0.5, helper.download_manager.GetPositiveUploadRate());
+ EXPECT_DOUBLE_EQ(0.3, helper.download_manager.GetNegativeUploadRate());
+
fetcher = factory.GetFetcherByID(2);
ASSERT_TRUE(fetcher);
fetcher->delegate()->OnURLFetchComplete(fetcher, GURL(), URLRequestStatus(),
@@ -214,15 +221,63 @@ TEST(AutoFillDownloadTest, QueryAndUploadTest) {
EXPECT_EQ(signature, helper.responses_.front().signature);
EXPECT_EQ(responses[0], helper.responses_.front().response);
helper.responses_.pop_front();
+
// Set upload to 0% so no new requests happen.
helper.download_manager.SetPositiveUploadRate(0.0);
helper.download_manager.SetNegativeUploadRate(0.0);
// No actual requests for the next two calls, as we set upload rate to 0%.
- EXPECT_TRUE(helper.download_manager.StartUploadRequest(*(form_structures[0]),
+ EXPECT_FALSE(helper.download_manager.StartUploadRequest(*(form_structures[0]),
true));
- EXPECT_TRUE(helper.download_manager.StartUploadRequest(*(form_structures[1]),
+ EXPECT_FALSE(helper.download_manager.StartUploadRequest(*(form_structures[1]),
false));
+ fetcher = factory.GetFetcherByID(3);
+ EXPECT_EQ(NULL, fetcher);
+
+ // Verify DOS attack back-offs.
+ const int kBackOffTimeout = 10000;
+
+ // Request with id 3.
+ EXPECT_TRUE(helper.download_manager.StartQueryRequest(form_structures));
+ fetcher = factory.GetFetcherByID(3);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_backoff_delay(
+ base::TimeDelta::FromMilliseconds(kBackOffTimeout));
+ fetcher->delegate()->OnURLFetchComplete(fetcher, GURL(), URLRequestStatus(),
+ 500, ResponseCookies(),
+ std::string(responses[0]));
+ EXPECT_EQ(AutoFillDownloadTestHelper::REQUEST_QUERY_FAILED,
+ helper.responses_.front().type_of_response);
+ EXPECT_EQ(500, helper.responses_.front().error);
+ // Expected response on non-query request is an empty string.
+ EXPECT_EQ(std::string(""), helper.responses_.front().response);
+ helper.responses_.pop_front();
+
+ // Query requests should be ignored for the next 10 seconds.
+ EXPECT_FALSE(helper.download_manager.StartQueryRequest(form_structures));
+ fetcher = factory.GetFetcherByID(4);
+ EXPECT_EQ(NULL, fetcher);
+
+ // Set upload to 100% so requests happen.
+ helper.download_manager.SetPositiveUploadRate(1.0);
+ // Request with id 4.
+ EXPECT_TRUE(helper.download_manager.StartUploadRequest(*(form_structures[0]),
+ true));
fetcher = factory.GetFetcherByID(4);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_backoff_delay(
+ base::TimeDelta::FromMilliseconds(kBackOffTimeout));
+ fetcher->delegate()->OnURLFetchComplete(fetcher, GURL(), URLRequestStatus(),
+ 503, ResponseCookies(),
+ std::string(responses[2]));
+ EXPECT_EQ(AutoFillDownloadTestHelper::REQUEST_UPLOAD_FAILED,
+ helper.responses_.front().type_of_response);
+ EXPECT_EQ(503, helper.responses_.front().error);
+ helper.responses_.pop_front();
+
+ // Upload requests should be ignored for the next 10 seconds.
+ EXPECT_FALSE(helper.download_manager.StartUploadRequest(*(form_structures[0]),
+ true));
+ fetcher = factory.GetFetcherByID(5);
EXPECT_EQ(NULL, fetcher);
// Make sure consumer of URLFetcher does the right thing.
diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc
index 628cb9a..5bed021 100644
--- a/chrome/browser/autofill/autofill_manager.cc
+++ b/chrome/browser/autofill/autofill_manager.cc
@@ -21,9 +21,18 @@
#include "webkit/glue/form_field.h"
#include "webkit/glue/form_field_values.h"
+namespace {
+// We only send a fraction of the forms to upload server.
+// The rate for positive/negative matches potentially could be different.
+const double kAutoFillPositiveUploadRateDefaultValue = 0.01;
+const double kAutoFillNegativeUploadRateDefaultValue = 0.01;
+} // namespace
+
+
AutoFillManager::AutoFillManager(TabContents* tab_contents)
: tab_contents_(tab_contents),
personal_data_(NULL),
+ download_manager_(tab_contents_->profile()),
infobar_(NULL) {
DCHECK(tab_contents);
@@ -52,6 +61,11 @@ void AutoFillManager::RegisterUserPrefs(PrefService* prefs) {
prefs->RegisterBooleanPref(prefs::kAutoFillAuxiliaryProfilesEnabled, false);
prefs->RegisterStringPref(prefs::kAutoFillDefaultProfile, std::wstring());
prefs->RegisterStringPref(prefs::kAutoFillDefaultCreditCard, std::wstring());
+
+ prefs->RegisterRealPref(prefs::kAutoFillPositiveUploadRate,
+ kAutoFillPositiveUploadRateDefaultValue);
+ prefs->RegisterRealPref(prefs::kAutoFillNegativeUploadRate,
+ kAutoFillNegativeUploadRateDefaultValue);
}
void AutoFillManager::FormFieldValuesSubmitted(
@@ -461,5 +475,6 @@ bool AutoFillManager::IsAutoFillEnabled() {
AutoFillManager::AutoFillManager()
: tab_contents_(NULL),
- personal_data_(NULL) {
+ personal_data_(NULL),
+ download_manager_(NULL) {
}
diff --git a/chrome/browser/autofill/autofill_xml_parser.h b/chrome/browser/autofill/autofill_xml_parser.h
index 1fda2fe..69c662a 100644
--- a/chrome/browser/autofill/autofill_xml_parser.h
+++ b/chrome/browser/autofill/autofill_xml_parser.h
@@ -100,7 +100,7 @@ class AutoFillQueryXmlParser : public AutoFillXmlParser {
// 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 {
+class AutoFillUploadXmlParser : public AutoFillXmlParser {
public:
AutoFillUploadXmlParser(double* positive_upload_rate,
double* negative_upload_rate);
diff --git a/chrome/browser/autofill/autofill_xml_parser_unittest.cc b/chrome/browser/autofill/autofill_xml_parser_unittest.cc
index 7e1e55c..c06df83 100644
--- a/chrome/browser/autofill/autofill_xml_parser_unittest.cc
+++ b/chrome/browser/autofill/autofill_xml_parser_unittest.cc
@@ -135,4 +135,59 @@ TEST(AutoFillQueryXmlParserTest, ParseErrors) {
EXPECT_EQ(NO_SERVER_DATA, field_types[0]);
}
+// Test successfull upload response.
+TEST(AutoFillUploadXmlParser, TestSuccessfulResponse) {
+ std::string xml = "<autofilluploadresponse positiveuploadrate=\"0.5\" "
+ "negativeuploadrate=\"0.3\"/>";
+ double positive = 0;
+ double negative = 0;
+ AutoFillUploadXmlParser parse_handler(&positive, &negative);
+ buzz::XmlParser parser(&parse_handler);
+ parser.Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler.succeeded());
+ EXPECT_DOUBLE_EQ(0.5, positive);
+ EXPECT_DOUBLE_EQ(0.3, negative);
+}
+
+// Test failed upload response.
+TEST(AutoFillUploadXmlParser, TestFailedResponse) {
+ std::string xml = "<autofilluploadresponse positiveuploadrate=\"\" "
+ "negativeuploadrate=\"0.3\"/>";
+ double positive = 0;
+ double negative = 0;
+ scoped_ptr<AutoFillUploadXmlParser> parse_handler(
+ new AutoFillUploadXmlParser(&positive, &negative));
+ 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_DOUBLE_EQ(0, positive);
+ EXPECT_DOUBLE_EQ(0.3, negative); // Partially parsed.
+ negative = 0;
+
+ xml = "<autofilluploadresponse positiveuploadrate=\"0.5\" "
+ "negativeuploadrate=\"0.3\"";
+ parse_handler.reset(new AutoFillUploadXmlParser(&positive, &negative));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(!parse_handler->succeeded());
+ EXPECT_DOUBLE_EQ(0, positive);
+ EXPECT_DOUBLE_EQ(0, negative);
+
+ xml = "bad data";
+ parse_handler.reset(new AutoFillUploadXmlParser(&positive, &negative));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(!parse_handler->succeeded());
+ EXPECT_DOUBLE_EQ(0, positive);
+ EXPECT_DOUBLE_EQ(0, negative);
+
+ xml = "";
+ parse_handler.reset(new AutoFillUploadXmlParser(&positive, &negative));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(!parse_handler->succeeded());
+ EXPECT_DOUBLE_EQ(0, positive);
+ EXPECT_DOUBLE_EQ(0, negative);
+}
+
} // namespace