Pop up requestAutocomplete UI when autofill server hints chrome client that it is in a multipage autofill flow.
* Added AutofillFlowInfobarDelegate for the Infobar. (Note that this might evolve soon when we have much concrete UX)
* Strings used are also temporary.
* browser/autofill/autofill_flow_util.* : helper functions to build FormStructure representing all the information needed for autofill_flow.
* browser/autofill/autofill_manager.* : Added new methods to pop the UI and callback method to handle data from UI.
* browser/autofill/autofill_xml_parser/form_structure: To get information about step in the autofill flow from autofill servers.
* renderer/autofill/autofill_agent.* : To send SSL Status of the page back to the browser process.
BUG=159830
Review URL: https://chromiumcodereview.appspot.com/11539003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@177746 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 3a2ea76..cd5923b 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10605,6 +10605,15 @@
<message name="IDS_AUTOFILL_CC_INFOBAR_DENY" desc="Text to show for the Autofill credit card request infobar deny button.">
Don't save
</message>
+ <message name="IDS_AUTOFILL_FLOW_INFOBAR_TEXT" desc="Text to show in the Autofill flow request infobar.">
+ Do you want to use Accelarated Autofill?
+ </message>
+ <message name="IDS_AUTOFILL_FLOW_INFOBAR_ACCEPT" desc="Text to show for the Autofill flow request infobar accept button.">
+ Accelarate
+ </message>
+ <message name="IDS_AUTOFILL_FLOW_INFOBAR_DENY" desc="Text to show for the Autofill flow request infobar deny button.">
+ No thanks
+ </message>
<message name="IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM" desc="The entry in the suggestions dropdown that clears an auto-filled form.">
Clear form
</message>
diff --git a/chrome/browser/autofill/autocheckout_infobar_delegate.cc b/chrome/browser/autofill/autocheckout_infobar_delegate.cc
new file mode 100644
index 0000000..b23b2e0
--- /dev/null
+++ b/chrome/browser/autofill/autocheckout_infobar_delegate.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2013 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/autocheckout_infobar_delegate.h"
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/api/infobars/infobar_service.h"
+#include "chrome/browser/autofill/autocheckout_manager.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/browser/page_navigator.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+// static
+void AutocheckoutInfoBarDelegate::Create(
+ const AutofillMetrics& metric_logger,
+ const GURL& source_url,
+ const content::SSLStatus& ssl_status,
+ AutocheckoutManager* autocheckout_manager,
+ InfoBarService* infobar_service) {
+ infobar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>(
+ new AutocheckoutInfoBarDelegate(metric_logger, source_url, ssl_status,
+ autocheckout_manager, infobar_service)));
+}
+
+AutocheckoutInfoBarDelegate::AutocheckoutInfoBarDelegate(
+ const AutofillMetrics& metric_logger,
+ const GURL& source_url,
+ const content::SSLStatus& ssl_status,
+ AutocheckoutManager* autocheckout_manager,
+ InfoBarService* infobar_service)
+ : ConfirmInfoBarDelegate(infobar_service),
+ metric_logger_(metric_logger),
+ autocheckout_manager_(autocheckout_manager),
+ source_url_(source_url),
+ ssl_status_(ssl_status),
+ had_user_interaction_(false) {
+ metric_logger_.LogAutocheckoutInfoBarMetric(AutofillMetrics::INFOBAR_SHOWN);
+}
+
+AutocheckoutInfoBarDelegate::~AutocheckoutInfoBarDelegate() {
+ if (!had_user_interaction_)
+ LogUserAction(AutofillMetrics::INFOBAR_IGNORED);
+}
+
+void AutocheckoutInfoBarDelegate::LogUserAction(
+ AutofillMetrics::InfoBarMetric user_action) {
+ DCHECK(!had_user_interaction_);
+ metric_logger_.LogAutocheckoutInfoBarMetric(user_action);
+ had_user_interaction_ = true;
+}
+
+void AutocheckoutInfoBarDelegate::InfoBarDismissed() {
+ LogUserAction(AutofillMetrics::INFOBAR_DENIED);
+}
+
+gfx::Image* AutocheckoutInfoBarDelegate::GetIcon() const {
+ return &ResourceBundle::GetSharedInstance().GetNativeImageNamed(
+ IDR_INFOBAR_AUTOFILL);
+}
+
+InfoBarDelegate::Type AutocheckoutInfoBarDelegate::GetInfoBarType() const {
+ return PAGE_ACTION_TYPE;
+}
+
+bool AutocheckoutInfoBarDelegate::ShouldExpireInternal(
+ const content::LoadCommittedDetails& details) const {
+ // The user has submitted a form, causing the page to navigate elsewhere. We
+ // want the infobar to be expired at this point, because the user has
+ // potentially started the checkout flow manually.
+ return true;
+}
+
+
+string16 AutocheckoutInfoBarDelegate::GetMessageText() const {
+ return l10n_util::GetStringUTF16(IDS_AUTOFILL_FLOW_INFOBAR_TEXT);
+}
+
+string16 AutocheckoutInfoBarDelegate::GetButtonLabel(
+ InfoBarButton button) const {
+ return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
+ IDS_AUTOFILL_FLOW_INFOBAR_ACCEPT : IDS_AUTOFILL_FLOW_INFOBAR_DENY);
+}
+
+bool AutocheckoutInfoBarDelegate::Accept() {
+ LogUserAction(AutofillMetrics::INFOBAR_ACCEPTED);
+ autocheckout_manager_->ShowAutocheckoutDialog(source_url_, ssl_status_);
+ return true;
+}
+
+bool AutocheckoutInfoBarDelegate::Cancel() {
+ LogUserAction(AutofillMetrics::INFOBAR_DENIED);
+ return true;
+}
+
+string16 AutocheckoutInfoBarDelegate::GetLinkText() const {
+ return l10n_util::GetStringUTF16(IDS_LEARN_MORE);
+}
+
+bool AutocheckoutInfoBarDelegate::LinkClicked(
+ WindowOpenDisposition disposition) {
+ // TODO(ramankk): Fix the help URL when we have one.
+ owner()->GetWebContents()->GetDelegate()->OpenURLFromTab(
+ owner()->GetWebContents(),
+ content::OpenURLParams(GURL(chrome::kAutofillHelpURL),
+ content::Referrer(),
+ NEW_FOREGROUND_TAB,
+ content::PAGE_TRANSITION_LINK,
+ false));
+ return false;
+}
+
diff --git a/chrome/browser/autofill/autocheckout_infobar_delegate.h b/chrome/browser/autofill/autocheckout_infobar_delegate.h
new file mode 100644
index 0000000..60c82f4
--- /dev/null
+++ b/chrome/browser/autofill/autocheckout_infobar_delegate.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2013 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_AUTOCHECKOUT_INFOBAR_DELEGATE_H_
+#define CHROME_BROWSER_AUTOFILL_AUTOCHECKOUT_INFOBAR_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "chrome/browser/api/infobars/confirm_infobar_delegate.h"
+#include "chrome/browser/autofill/autofill_metrics.h"
+#include "content/public/common/ssl_status.h"
+#include "googleurl/src/gurl.h"
+
+class AutocheckoutManager;
+
+namespace content {
+struct LoadCommittedDetails;
+}
+
+// An InfoBar delegate that enables the user to allow or deny storing credit
+// card information gathered from a form submission.
+class AutocheckoutInfoBarDelegate : public ConfirmInfoBarDelegate {
+ public:
+ // Creates an autofillflow infobar delegate and adds it to |infobar_service|.
+ static void Create(const AutofillMetrics& metric_logger,
+ const GURL& source_url,
+ const content::SSLStatus& ssl_status,
+ AutocheckoutManager* autocheckout_manager,
+ InfoBarService* infobar_service);
+
+#if defined(UNIT_TEST)
+ static scoped_ptr<ConfirmInfoBarDelegate> Create(
+ const AutofillMetrics& metric_logger,
+ const GURL& source_url,
+ const content::SSLStatus& ssl_status,
+ AutocheckoutManager* autocheckout_manager) {
+ return scoped_ptr<ConfirmInfoBarDelegate>(new AutocheckoutInfoBarDelegate(
+ metric_logger, source_url, ssl_status, autocheckout_manager, NULL));
+ }
+#endif
+
+ private:
+ AutocheckoutInfoBarDelegate(const AutofillMetrics& metric_logger,
+ const GURL& source_url,
+ const content::SSLStatus& ssl_status,
+ AutocheckoutManager* autocheckout_manager,
+ InfoBarService* infobar_service);
+
+ virtual ~AutocheckoutInfoBarDelegate();
+
+ // Logs UMA metric for user action type.
+ void LogUserAction(AutofillMetrics::InfoBarMetric user_action);
+
+ // ConfirmInfoBarDelegate:
+ virtual void InfoBarDismissed() OVERRIDE;
+ virtual gfx::Image* GetIcon() const OVERRIDE;
+ virtual Type GetInfoBarType() const OVERRIDE;
+ virtual bool ShouldExpireInternal(
+ const content::LoadCommittedDetails& details) const OVERRIDE;
+ virtual string16 GetMessageText() const OVERRIDE;
+ virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
+ virtual bool Accept() OVERRIDE;
+ virtual bool Cancel() OVERRIDE;
+ virtual string16 GetLinkText() const OVERRIDE;
+ virtual bool LinkClicked(WindowOpenDisposition disposition) OVERRIDE;
+
+ // For logging UMA metrics.
+ // Weak reference. Owned by the AutofillManager that initiated this infobar.
+ const AutofillMetrics& metric_logger_;
+
+ // To callback AutocheckoutManager's ShowAutocheckoutDialog.
+ AutocheckoutManager* autocheckout_manager_;
+
+ // URL of the page which triggered infobar.
+ GURL source_url_;
+
+ // SSL status of the page which triggered infobar.
+ content::SSLStatus ssl_status_;
+
+ // Did the user ever explicitly accept or dismiss this infobar?
+ bool had_user_interaction_;
+
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AutocheckoutInfoBar);
+
+ DISALLOW_COPY_AND_ASSIGN(AutocheckoutInfoBarDelegate);
+};
+
+#endif // CHROME_BROWSER_AUTOFILL_AUTOCHECKOUT_INFOBAR_DELEGATE_H_
diff --git a/chrome/browser/autofill/autocheckout_manager.cc b/chrome/browser/autofill/autocheckout_manager.cc
new file mode 100644
index 0000000..3c623affe
--- /dev/null
+++ b/chrome/browser/autofill/autocheckout_manager.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2013 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/autocheckout_manager.h"
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "chrome/browser/autofill/autofill_manager.h"
+#include "chrome/browser/ui/autofill/autofill_dialog_controller_impl.h"
+#include "chrome/common/form_data.h"
+#include "chrome/common/form_field_data.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/ssl_status.h"
+#include "googleurl/src/gurl.h"
+
+using content::SSLStatus;
+using content::WebContents;
+
+namespace {
+
+// Build FormFieldData based on the supplied |autocomplete_attribute|. Will
+// fill rest of properties with default values.
+FormFieldData BuildField(const std::string& autocomplete_attribute) {
+ FormFieldData field;
+ field.name = string16();
+ field.value = string16();
+ field.autocomplete_attribute = autocomplete_attribute;
+ field.form_control_type = "text";
+ return field;
+}
+
+// Build Autocheckout specific form data to be consumed by
+// AutofillDialogController to show the Autocheckout specific UI.
+FormData BuildAutocheckoutFormData() {
+ FormData formdata;
+ formdata.fields.push_back(BuildField("name"));
+ formdata.fields.push_back(BuildField("email"));
+ formdata.fields.push_back(BuildField("cc-name"));
+ formdata.fields.push_back(BuildField("cc-number"));
+ formdata.fields.push_back(BuildField("cc-exp"));
+ formdata.fields.push_back(BuildField("cc-csc"));
+ formdata.fields.push_back(BuildField("billing street-address"));
+ formdata.fields.push_back(BuildField("billing locality"));
+ formdata.fields.push_back(BuildField("billing region"));
+ formdata.fields.push_back(BuildField("billing country"));
+ formdata.fields.push_back(BuildField("billing postal-code"));
+ formdata.fields.push_back(BuildField("shipping street-address"));
+ formdata.fields.push_back(BuildField("shipping locality"));
+ formdata.fields.push_back(BuildField("shipping region"));
+ formdata.fields.push_back(BuildField("shipping country"));
+ formdata.fields.push_back(BuildField("shipping postal-code"));
+ return formdata;
+}
+
+} // namespace
+
+AutocheckoutManager::AutocheckoutManager(AutofillManager* autofill_manager)
+ : autofill_manager_(autofill_manager),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
+}
+
+AutocheckoutManager::~AutocheckoutManager() {
+}
+
+void AutocheckoutManager::ShowAutocheckoutDialog(
+ const GURL& frame_url,
+ const SSLStatus& ssl_status) {
+ base::Callback<void(const FormStructure*)> callback =
+ base::Bind(&AutocheckoutManager::ReturnAutocheckoutData,
+ weak_ptr_factory_.GetWeakPtr());
+ autofill::AutofillDialogControllerImpl* controller =
+ new autofill::AutofillDialogControllerImpl(
+ autofill_manager_->GetWebContents(),
+ BuildAutocheckoutFormData(),
+ frame_url,
+ ssl_status,
+ callback);
+ controller->Show();
+}
+
+void AutocheckoutManager::ReturnAutocheckoutData(const FormStructure* result) {
+ // TODO(ramankk): Parse the response FormStructure.
+}
diff --git a/chrome/browser/autofill/autocheckout_manager.h b/chrome/browser/autofill/autocheckout_manager.h
new file mode 100644
index 0000000..548042b
--- /dev/null
+++ b/chrome/browser/autofill/autocheckout_manager.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2013 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_AUTOCHECKOUT_MANAGER_H_
+#define CHROME_BROWSER_AUTOFILL_AUTOCHECKOUT_MANAGER_H_
+
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+
+class AutofillManager;
+class FormStructure;
+class GURL;
+
+struct FormData;
+
+namespace content {
+struct SSLStatus;
+}
+
+class AutocheckoutManager {
+ public:
+ explicit AutocheckoutManager(AutofillManager* autofill_manager);
+
+ virtual void ShowAutocheckoutDialog(const GURL& frame_url,
+ const content::SSLStatus& ssl_status);
+
+ virtual ~AutocheckoutManager();
+
+ private:
+
+ // Callback called from AutofillDialogController on filling up the UI form.
+ void ReturnAutocheckoutData(const FormStructure* result);
+
+ AutofillManager* autofill_manager_; // WEAK; owns us
+ base::WeakPtrFactory<AutocheckoutManager> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutocheckoutManager);
+};
+
+#endif // CHROME_BROWSER_AUTOFILL_AUTOCHECKOUT_MANAGER_H_
diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc
index 7da1731..90f6874 100644
--- a/chrome/browser/autofill/autofill_manager.cc
+++ b/chrome/browser/autofill/autofill_manager.cc
@@ -23,6 +23,8 @@
#include "base/utf_string_conversions.h"
#include "chrome/browser/api/infobars/infobar_service.h"
#include "chrome/browser/api/sync/profile_sync_service_base.h"
+#include "chrome/browser/autofill/autocheckout_manager.h"
+#include "chrome/browser/autofill/autocheckout_infobar_delegate.h"
#include "chrome/browser/autofill/autocomplete_history_manager.h"
#include "chrome/browser/autofill/autofill_cc_infobar_delegate.h"
#include "chrome/browser/autofill/autofill_country.h"
@@ -200,6 +202,7 @@
download_manager_(delegate->GetBrowserContext(), this),
disable_download_manager_requests_(false),
autocomplete_history_manager_(web_contents),
+ autocheckout_manager_(this),
metric_logger_(new AutofillMetrics),
has_logged_autofill_enabled_(false),
has_logged_address_suggestions_count_(false),
@@ -575,6 +578,18 @@
}
}
}
+
+ // If form is known to be at the start of the autofillable flow (i.e, when
+ // Autofill server said so), then trigger payments UI while also returning
+ // standard autofill suggestions to renderer process.
+ if (form_structure->IsStartOfAutofillableFlow()) {
+ AutocheckoutInfoBarDelegate::Create(
+ *metric_logger_,
+ form.origin,
+ form.ssl_status,
+ &autocheckout_manager_,
+ manager_delegate_->GetInfoBarService());
+ }
}
// Add the results from AutoComplete. They come back asynchronously, so we
@@ -749,6 +764,10 @@
autocomplete_history_manager_.OnRemoveAutocompleteEntry(name, value);
}
+content::WebContents* AutofillManager::GetWebContents() {
+ return web_contents();
+}
+
void AutofillManager::OnAddPasswordFormMapping(
const FormFieldData& form,
const PasswordFormFillData& fill_data) {
@@ -947,6 +966,7 @@
download_manager_(delegate->GetBrowserContext(), this),
disable_download_manager_requests_(true),
autocomplete_history_manager_(web_contents),
+ autocheckout_manager_(this),
metric_logger_(new AutofillMetrics),
has_logged_autofill_enabled_(false),
has_logged_address_suggestions_count_(false),
diff --git a/chrome/browser/autofill/autofill_manager.h b/chrome/browser/autofill/autofill_manager.h
index e2edbd5b..0c2c8fb1 100644
--- a/chrome/browser/autofill/autofill_manager.h
+++ b/chrome/browser/autofill/autofill_manager.h
@@ -21,12 +21,14 @@
#include "base/string16.h"
#include "base/time.h"
#include "chrome/browser/api/sync/profile_sync_service_observer.h"
+#include "chrome/browser/autofill/autocheckout_manager.h"
#include "chrome/browser/autofill/autocomplete_history_manager.h"
#include "chrome/browser/autofill/autofill_download.h"
#include "chrome/browser/autofill/field_types.h"
#include "chrome/browser/autofill/form_structure.h"
#include "chrome/browser/autofill/personal_data_manager.h"
#include "chrome/common/autofill/autocheckout_status.h"
+#include "chrome/common/form_data.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/ssl_status.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h"
@@ -107,6 +109,9 @@
// Remove the specified Autocomplete entry.
void RemoveAutocompleteEntry(const string16& name, const string16& value);
+ // Returns the present web_contents state.
+ content::WebContents* GetWebContents();
+
protected:
// Only test code should subclass AutofillManager.
friend class base::RefCounted<AutofillManager>;
@@ -336,6 +341,9 @@
// Handles single-field autocomplete form data.
AutocompleteHistoryManager autocomplete_history_manager_;
+ // Handles autocheckout flows.
+ AutocheckoutManager autocheckout_manager_;
+
// For logging UMA metrics. Overridden by metrics tests.
scoped_ptr<const AutofillMetrics> metric_logger_;
// Have we logged whether Autofill is enabled for this page load?
diff --git a/chrome/browser/autofill/autofill_metrics.cc b/chrome/browser/autofill/autofill_metrics.cc
index c806a9849..24c9e706 100644
--- a/chrome/browser/autofill/autofill_metrics.cc
+++ b/chrome/browser/autofill/autofill_metrics.cc
@@ -261,6 +261,13 @@
NUM_INFO_BAR_METRICS);
}
+void AutofillMetrics::LogAutocheckoutInfoBarMetric(InfoBarMetric metric) const {
+ DCHECK(metric < NUM_INFO_BAR_METRICS);
+
+ UMA_HISTOGRAM_ENUMERATION("Autofill.AutocheckoutInfoBar", metric,
+ NUM_INFO_BAR_METRICS);
+}
+
void AutofillMetrics::LogDeveloperEngagementMetric(
DeveloperEngagementMetric metric) const {
DCHECK(metric < NUM_DEVELOPER_ENGAGEMENT_METRICS);
diff --git a/chrome/browser/autofill/autofill_metrics.h b/chrome/browser/autofill/autofill_metrics.h
index f270c7a0..b7143ba 100644
--- a/chrome/browser/autofill/autofill_metrics.h
+++ b/chrome/browser/autofill/autofill_metrics.h
@@ -156,6 +156,8 @@
virtual void LogUserHappinessMetric(UserHappinessMetric metric) const;
+ virtual void LogAutocheckoutInfoBarMetric(InfoBarMetric metric) const;
+
// This should be called when a form that has been Autofilled is submitted.
// |duration| should be the time elapsed between form load and submission.
virtual void LogFormFillDurationFromLoadWithAutofill(
diff --git a/chrome/browser/autofill/autofill_metrics_unittest.cc b/chrome/browser/autofill/autofill_metrics_unittest.cc
index 40fb33fe..9946a5d 100644
--- a/chrome/browser/autofill/autofill_metrics_unittest.cc
+++ b/chrome/browser/autofill/autofill_metrics_unittest.cc
@@ -8,6 +8,7 @@
#include "base/string16.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
+#include "chrome/browser/autofill/autocheckout_infobar_delegate.h"
#include "chrome/browser/autofill/autofill_cc_infobar_delegate.h"
#include "chrome/browser/autofill/autofill_common_test.h"
#include "chrome/browser/autofill/autofill_manager.h"
@@ -22,6 +23,7 @@
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/test_browser_thread.h"
+#include "googleurl/src/gurl.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/rect.h"
@@ -39,6 +41,7 @@
public:
MockAutofillMetrics() {}
MOCK_CONST_METHOD1(LogCreditCardInfoBarMetric, void(InfoBarMetric metric));
+ MOCK_CONST_METHOD1(LogAutocheckoutInfoBarMetric, void(InfoBarMetric metric));
MOCK_CONST_METHOD1(LogDeveloperEngagementMetric,
void(DeveloperEngagementMetric metric));
MOCK_CONST_METHOD3(LogHeuristicTypePrediction,
@@ -257,6 +260,25 @@
DISALLOW_COPY_AND_ASSIGN(TestAutofillManager);
};
+class TestAutocheckoutManager : public AutocheckoutManager {
+ public:
+ explicit TestAutocheckoutManager(AutofillManager* autofill_manager)
+ : AutocheckoutManager(autofill_manager) {
+ }
+
+ virtual void ShowAutocheckoutDialog(
+ const GURL& frame_url,
+ const content::SSLStatus& ssl_status) OVERRIDE {
+ // no-op. Just used as callback from autocheckout_infobar_delegate.
+ }
+
+ virtual ~TestAutocheckoutManager() {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestAutocheckoutManager);
+};
+
} // namespace
class AutofillMetricsTest : public ChromeRenderViewHostTestHarness {
@@ -272,6 +294,9 @@
MockAutofillMetrics* metric_logger,
CreditCard** created_card);
+ scoped_ptr<ConfirmInfoBarDelegate> CreateAutocheckoutDelegate(
+ MockAutofillMetrics* metric_logger);
+
content::TestBrowserThread ui_thread_;
content::TestBrowserThread file_thread_;
@@ -347,6 +372,20 @@
metric_logger);
}
+scoped_ptr<ConfirmInfoBarDelegate>
+AutofillMetricsTest::CreateAutocheckoutDelegate(
+ MockAutofillMetrics* metric_logger) {
+ EXPECT_CALL(*metric_logger,
+ LogAutocheckoutInfoBarMetric(AutofillMetrics::INFOBAR_SHOWN));
+ GURL url("www.google.com");
+ content::SSLStatus ssl_status;
+ return AutocheckoutInfoBarDelegate::Create(
+ *metric_logger,
+ url,
+ ssl_status,
+ new TestAutocheckoutManager(NULL));
+}
+
// Test that we log quality metrics appropriately.
TEST_F(AutofillMetricsTest, QualityMetrics) {
// Set up our form data.
@@ -1186,6 +1225,65 @@
}
}
+// Test that autofill flow infobar metrics are logged correctly.
+TEST_F(AutofillMetricsTest, AutocheckoutInfoBar) {
+ MockAutofillMetrics metric_logger;
+ ::testing::InSequence dummy;
+
+ // Accept the infobar.
+ {
+ scoped_ptr<ConfirmInfoBarDelegate> infobar(
+ CreateAutocheckoutDelegate(&metric_logger));
+ ASSERT_TRUE(infobar);
+ EXPECT_CALL(metric_logger,
+ LogAutocheckoutInfoBarMetric(
+ AutofillMetrics::INFOBAR_ACCEPTED)).Times(1);
+ EXPECT_CALL(metric_logger,
+ LogAutocheckoutInfoBarMetric(
+ AutofillMetrics::INFOBAR_IGNORED)).Times(0);
+ EXPECT_TRUE(infobar->Accept());
+ }
+
+ // Cancel the infobar.
+ {
+ scoped_ptr<ConfirmInfoBarDelegate> infobar(
+ CreateAutocheckoutDelegate(&metric_logger));
+ ASSERT_TRUE(infobar);
+ EXPECT_CALL(metric_logger,
+ LogAutocheckoutInfoBarMetric(
+ AutofillMetrics::INFOBAR_DENIED)).Times(1);
+ EXPECT_CALL(metric_logger,
+ LogAutocheckoutInfoBarMetric(
+ AutofillMetrics::INFOBAR_IGNORED)).Times(0);
+ EXPECT_TRUE(infobar->Cancel());
+ }
+
+ // Dismiss the infobar.
+ {
+ scoped_ptr<ConfirmInfoBarDelegate> infobar(
+ CreateAutocheckoutDelegate(&metric_logger));
+ ASSERT_TRUE(infobar);
+ EXPECT_CALL(metric_logger,
+ LogAutocheckoutInfoBarMetric(
+ AutofillMetrics::INFOBAR_DENIED)).Times(1);
+ EXPECT_CALL(metric_logger,
+ LogAutocheckoutInfoBarMetric(
+ AutofillMetrics::INFOBAR_IGNORED)).Times(0);
+ infobar->InfoBarDismissed();
+ }
+
+ // Ignore the infobar.
+ {
+ scoped_ptr<ConfirmInfoBarDelegate> infobar(
+ CreateAutocheckoutDelegate(&metric_logger));
+ ASSERT_TRUE(infobar);
+ EXPECT_CALL(metric_logger,
+ LogAutocheckoutInfoBarMetric(
+ AutofillMetrics::INFOBAR_IGNORED)).Times(1);
+ }
+}
+
+
// Test that server query response experiment id metrics are logged correctly.
TEST_F(AutofillMetricsTest, ServerQueryExperimentIdForQuery) {
MockAutofillMetrics metric_logger;
diff --git a/chrome/browser/autofill/autofill_xml_parser.cc b/chrome/browser/autofill/autofill_xml_parser.cc
index 92a1ede9..425185b7 100644
--- a/chrome/browser/autofill/autofill_xml_parser.cc
+++ b/chrome/browser/autofill/autofill_xml_parser.cc
@@ -33,6 +33,8 @@
std::string* experiment_id)
: field_types_(field_types),
upload_required_(upload_required),
+ current_page_number_(-1),
+ total_pages_(-1),
experiment_id_(experiment_id) {
DCHECK(upload_required_);
DCHECK(experiment_id_);
@@ -89,6 +91,18 @@
// Record this field type.
field_types_->push_back(field_type);
+ } else if (element.compare("autofill_flow") == 0) {
+ // |attrs| is a NULL-terminated list of (attribute, value) pairs.
+ while (*attrs) {
+ buzz::QName attribute_qname = context->ResolveQName(*attrs, true);
+ ++attrs;
+ const std::string& attribute_name = attribute_qname.LocalPart();
+ if (attribute_name.compare("page_no") == 0)
+ current_page_number_ = GetIntValue(context, *attrs);
+ else if (attribute_name.compare("total_pages") == 0)
+ total_pages_ = GetIntValue(context, *attrs);
+ ++attrs;
+ }
}
}
diff --git a/chrome/browser/autofill/autofill_xml_parser.h b/chrome/browser/autofill/autofill_xml_parser.h
index da3d5bf6..42df566 100644
--- a/chrome/browser/autofill/autofill_xml_parser.h
+++ b/chrome/browser/autofill/autofill_xml_parser.h
@@ -70,6 +70,10 @@
UploadRequired* upload_required,
std::string* experiment_id);
+ int current_page_number() const { return current_page_number_; }
+
+ int total_pages() const { return total_pages_; }
+
private:
// A callback for the beginning of a new <element>, called by Expat.
// |context| is a parsing context used to resolve element/attribute names.
@@ -92,6 +96,12 @@
// form is submitted.
UploadRequired* upload_required_;
+ // Page number of present page in multipage autofill flow.
+ int current_page_number_;
+
+ // Total number of pages in multipage autofill flow.
+ int total_pages_;
+
// 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 a0a1024..44ea704 100644
--- a/chrome/browser/autofill/autofill_xml_parser_unittest.cc
+++ b/chrome/browser/autofill/autofill_xml_parser_unittest.cc
@@ -154,6 +154,28 @@
EXPECT_EQ(std::string("ServerSmartyPants"), experiment_id);
}
+// Test XML response with autofill_flow information.
+TEST(AutofillQueryXmlParserTest, ParseAutofillFlow) {
+ std::vector<AutofillFieldType> field_types;
+ UploadRequired upload_required = USE_UPLOAD_RATES;
+ std::string experiment_id;
+
+ std::string xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"55\"/>"
+ "<autofill_flow page_no=\"1\" total_pages=\"10\"/>"
+ "</autofillqueryresponse>";
+
+ scoped_ptr<AutofillQueryXmlParser> parse_handler(
+ new AutofillQueryXmlParser(&field_types, &upload_required,
+ &experiment_id));
+ 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(1U, field_types.size());
+ EXPECT_EQ(1, parse_handler->current_page_number());
+ EXPECT_EQ(10, parse_handler->total_pages());
+}
+
// Test badly formed XML queries.
TEST(AutofillQueryXmlParserTest, ParseErrors) {
std::vector<AutofillFieldType> field_types;
diff --git a/chrome/browser/autofill/form_structure.cc b/chrome/browser/autofill/form_structure.cc
index d1ca6d1..8852240 100644
--- a/chrome/browser/autofill/form_structure.cc
+++ b/chrome/browser/autofill/form_structure.cc
@@ -229,6 +229,8 @@
autofill_count_(0),
upload_required_(USE_UPLOAD_RATES),
server_experiment_id_("no server response"),
+ current_page_number_(-1),
+ total_pages_(-1),
has_author_specified_types_(false) {
// Copy the form fields.
std::map<string16, size_t> unique_names;
@@ -429,6 +431,8 @@
FormStructure* form = *iter;
form->upload_required_ = upload_required;
form->server_experiment_id_ = experiment_id;
+ form->current_page_number_ = parse_handler.current_page_number();
+ form->total_pages_ = parse_handler.total_pages();
for (std::vector<AutofillField*>::iterator field = form->fields_.begin();
field != form->fields_.end(); ++field, ++current_type) {
@@ -1016,6 +1020,14 @@
}
}
+bool FormStructure::IsStartOfAutofillableFlow() const {
+ return current_page_number_ == 0 && total_pages_ > 0;
+}
+
+bool FormStructure::IsInAutofillableFlow() const {
+ return current_page_number_ >= 0 && current_page_number_ < total_pages_;
+}
+
void FormStructure::IdentifySections(bool has_author_specified_sections) {
if (fields_.empty())
return;
diff --git a/chrome/browser/autofill/form_structure.h b/chrome/browser/autofill/form_structure.h
index 45cf2bb..2b47756 100644
--- a/chrome/browser/autofill/form_structure.h
+++ b/chrome/browser/autofill/form_structure.h
@@ -124,6 +124,14 @@
void ParseFieldTypesFromAutocompleteAttributes(bool* found_types,
bool* found_sections);
+ // Returns true if the autofill server says that the current page is start of
+ // the autofillable flow.
+ bool IsStartOfAutofillableFlow() const;
+
+ // Returns true if the autofill server says that the current page is in the
+ // autofillable flow.
+ bool IsInAutofillableFlow() const;
+
const AutofillField* field(size_t index) const;
AutofillField* field(size_t index);
size_t field_count() const;
@@ -208,6 +216,14 @@
// GET or POST.
RequestMethod method_;
+ // Page number of the autofill flow this form belongs to (zero-indexed).
+ // If this form doesn't belong to any autofill flow, it is set to -1.
+ int current_page_number_;
+
+ // Total number of pages in the autofill flow. If this form doesn't belong
+ // to any autofill flow, it is set to -1.
+ int total_pages_;
+
// Whether the form includes any field types explicitly specified by the site
// author, via the |autocompletetype| attribute.
bool has_author_specified_types_;
diff --git a/chrome/browser/autofill/form_structure_unittest.cc b/chrome/browser/autofill/form_structure_unittest.cc
index bd2bded..7ea1012 100644
--- a/chrome/browser/autofill/form_structure_unittest.cc
+++ b/chrome/browser/autofill/form_structure_unittest.cc
@@ -59,6 +59,13 @@
static std::string Hash64Bit(const std::string& str) {
return FormStructure::Hash64Bit(str);
}
+
+ static void SetPageDetails(FormStructure* form,
+ int page_number,
+ int total_pages) {
+ form->current_page_number_ = page_number;
+ form->total_pages_ = total_pages;
+ }
};
TEST(FormStructureTest, FieldCount) {
@@ -87,6 +94,41 @@
EXPECT_EQ(3U, form_structure.field_count());
}
+TEST(FormStructureTest, AutofillFlowInfo) {
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.label = ASCIIToUTF16("username");
+ field.name = ASCIIToUTF16("username");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form);
+ EXPECT_FALSE(form_structure.IsStartOfAutofillableFlow());
+ EXPECT_FALSE(form_structure.IsInAutofillableFlow());
+
+ FormStructureTest::SetPageDetails(&form_structure, -1, 0);
+ EXPECT_FALSE(form_structure.IsStartOfAutofillableFlow());
+ EXPECT_FALSE(form_structure.IsInAutofillableFlow());
+
+ FormStructureTest::SetPageDetails(&form_structure, 0, 0);
+ EXPECT_FALSE(form_structure.IsStartOfAutofillableFlow());
+ EXPECT_FALSE(form_structure.IsInAutofillableFlow());
+
+ FormStructureTest::SetPageDetails(&form_structure, 0, 1);
+ EXPECT_TRUE(form_structure.IsStartOfAutofillableFlow());
+ EXPECT_TRUE(form_structure.IsInAutofillableFlow());
+
+ FormStructureTest::SetPageDetails(&form_structure, 1, 2);
+ EXPECT_FALSE(form_structure.IsStartOfAutofillableFlow());
+ EXPECT_TRUE(form_structure.IsInAutofillableFlow());
+
+ FormStructureTest::SetPageDetails(&form_structure, 2, 2);
+ EXPECT_FALSE(form_structure.IsStartOfAutofillableFlow());
+ EXPECT_FALSE(form_structure.IsInAutofillableFlow());
+}
+
TEST(FormStructureTest, AutofillCount) {
FormData form;
form.method = ASCIIToUTF16("post");
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index d6f7261..7a7c0ca 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -176,6 +176,10 @@
'browser/autofill/address.h',
'browser/autofill/address_field.cc',
'browser/autofill/address_field.h',
+ 'browser/autofill/autocheckout_infobar_delegate.cc',
+ 'browser/autofill/autocheckout_infobar_delegate.h',
+ 'browser/autofill/autocheckout_manager.cc',
+ 'browser/autofill/autocheckout_manager.h',
'browser/autofill/autocomplete_history_manager.cc',
'browser/autofill/autocomplete_history_manager.h',
'browser/autofill/autofill-inl.h',
diff --git a/chrome/common/form_data.cc b/chrome/common/form_data.cc
index a78d4514..d857a14b 100644
--- a/chrome/common/form_data.cc
+++ b/chrome/common/form_data.cc
@@ -16,7 +16,8 @@
origin(data.origin),
action(data.action),
user_submitted(data.user_submitted),
- fields(data.fields) {
+ fields(data.fields),
+ ssl_status(data.ssl_status) {
}
FormData::~FormData() {
@@ -28,7 +29,8 @@
origin == form.origin &&
action == form.action &&
user_submitted == form.user_submitted &&
- fields == form.fields);
+ fields == form.fields &&
+ ssl_status.Equals(form.ssl_status));
}
bool FormData::operator!=(const FormData& form) const {
diff --git a/chrome/common/form_data.h b/chrome/common/form_data.h
index 018c9f9..40d9434 100644
--- a/chrome/common/form_data.h
+++ b/chrome/common/form_data.h
@@ -9,10 +9,19 @@
#include "base/string16.h"
#include "chrome/common/form_field_data.h"
+#include "content/public/common/ssl_status.h"
#include "googleurl/src/gurl.h"
// Holds information about a form to be filled and/or submitted.
struct FormData {
+ FormData();
+ FormData(const FormData& data);
+ ~FormData();
+
+ // Used by FormStructureTest.
+ bool operator==(const FormData& form) const;
+ bool operator!=(const FormData& form) const;
+
// The name of the form.
string16 name;
// GET or POST.
@@ -25,14 +34,8 @@
bool user_submitted;
// A vector of all the input fields in the form.
std::vector<FormFieldData> fields;
-
- FormData();
- FormData(const FormData& data);
- ~FormData();
-
- // Used by FormStructureTest.
- bool operator==(const FormData& form) const;
- bool operator!=(const FormData& form) const;
+ // SSL status of the frame contatining the form.
+ content::SSLStatus ssl_status;
};
#endif // CHROME_COMMON_FORM_DATA_H__
diff --git a/chrome/renderer/autofill/autofill_agent.cc b/chrome/renderer/autofill/autofill_agent.cc
index b20be00..820523c 100644
--- a/chrome/renderer/autofill/autofill_agent.cc
+++ b/chrome/renderer/autofill/autofill_agent.cc
@@ -236,6 +236,7 @@
HidePopups();
in_flight_request_form_ = form;
+ // TODO(ramankk): Include SSLStatus within form_data and update the IPC.
Send(new AutofillHostMsg_RequestAutocomplete(
routing_id(),
form_data,
@@ -713,6 +714,10 @@
data_list_icons,
data_list_unique_ids));
+ // Add SSL Status in the formdata to let browser process alert user
+ // appropriately using browser UI.
+ form.ssl_status = render_view()->GetSSLStatusOfFrame(
+ element.document().frame());
Send(new AutofillHostMsg_QueryFormFieldAutofill(routing_id(),
autofill_query_id_,
form,