summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorisherman@chromium.org <isherman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-13 05:06:47 +0000
committerisherman@chromium.org <isherman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-13 05:06:47 +0000
commit3c33b226aca0de240147a1b95e300d312369b730 (patch)
tree715c5d1e3038e1cb2917c20d7b88ec2176634735
parent9decd75f992e532b8bb5bc205d58dddab739277b (diff)
downloadchromium_src-3c33b226aca0de240147a1b95e300d312369b730.zip
chromium_src-3c33b226aca0de240147a1b95e300d312369b730.tar.gz
chromium_src-3c33b226aca0de240147a1b95e300d312369b730.tar.bz2
[Autofill] Metrics for dialog UI user interactions.
BUG=165570 Review URL: https://chromiumcodereview.appspot.com/12413007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@194087 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/ui/autofill/autofill_dialog_controller.h4
-rw-r--r--chrome/browser/ui/autofill/autofill_dialog_controller_browsertest.cc5
-rw-r--r--chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc142
-rw-r--r--chrome/browser/ui/autofill/autofill_dialog_controller_impl.h54
-rw-r--r--chrome/browser/ui/autofill/autofill_dialog_models.cc23
-rw-r--r--chrome/browser/ui/autofill/autofill_dialog_models.h10
-rw-r--r--chrome/browser/ui/autofill/autofill_dialog_models_unittest.cc29
-rw-r--r--chrome/browser/ui/autofill/autofill_dialog_types.cc69
-rw-r--r--chrome/browser/ui/autofill/autofill_dialog_types.h24
-rw-r--r--chrome/browser/ui/views/autofill/autofill_dialog_views.cc12
-rw-r--r--chrome/browser/ui/views/autofill/autofill_dialog_views.h4
-rw-r--r--components/autofill/browser/autofill_metrics.cc13
-rw-r--r--components/autofill/browser/autofill_metrics.h87
-rw-r--r--tools/metrics/histograms/histograms.xml59
14 files changed, 466 insertions, 69 deletions
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller.h b/chrome/browser/ui/autofill/autofill_dialog_controller.h
index 58d0eae..8300760 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_controller.h
+++ b/chrome/browser/ui/autofill/autofill_dialog_controller.h
@@ -125,12 +125,12 @@ class AutofillDialogController {
// Decides whether input of |value| is valid for a field of type |type|.
virtual bool InputIsValid(AutofillFieldType type,
- const string16& value) = 0;
+ const string16& value) const = 0;
// Decides whether the combination of all |inputs| is valid, returns a
// vector of all invalid fields.
virtual std::vector<AutofillFieldType> InputsAreValid(
- const DetailOutputMap& inputs, ValidationType validation_type) = 0;
+ const DetailOutputMap& inputs, ValidationType validation_type) const = 0;
// Called when the user changes the contents of a text field or activates it
// (by focusing and then clicking it). |was_edit| is true when the function
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_browsertest.cc b/chrome/browser/ui/autofill/autofill_dialog_controller_browsertest.cc
index 8012838..73f8f92 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_controller_browsertest.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_controller_browsertest.cc
@@ -92,12 +92,13 @@ class TestAutofillDialogController : public AutofillDialogControllerImpl {
}
virtual bool InputIsValid(AutofillFieldType type,
- const string16& value) OVERRIDE {
+ const string16& value) const OVERRIDE {
return true;
}
virtual std::vector<AutofillFieldType> InputsAreValid(
- const DetailOutputMap& inputs, ValidationType validation_type) OVERRIDE {
+ const DetailOutputMap& inputs,
+ ValidationType validation_type) const OVERRIDE {
return std::vector<AutofillFieldType>();
}
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc
index b6ae652..c548cbc 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc
@@ -268,10 +268,12 @@ void AutofillDialogControllerImpl::Show() {
const GURL& active_url = entry ? entry->GetURL() : contents_->GetURL();
invoked_from_same_origin_ = active_url.GetOrigin() == source_url_.GetOrigin();
- // Log any relevant security exceptions.
+ // Log any relevant UI metrics and security exceptions.
+ GetMetricLogger().LogDialogUiEvent(
+ dialog_type_, AutofillMetrics::DIALOG_UI_SHOWN);
+
GetMetricLogger().LogDialogSecurityMetric(
- dialog_type_,
- AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN);
+ dialog_type_, AutofillMetrics::SECURITY_METRIC_DIALOG_SHOWN);
if (RequestingCreditCardInfo() && !TransmissionWillBeSecure()) {
GetMetricLogger().LogDialogSecurityMetric(
@@ -479,7 +481,7 @@ string16 AutofillDialogControllerImpl::SignInLinkText() const {
}
bool AutofillDialogControllerImpl::ShouldOfferToSaveInChrome() const {
- return !IsPayingWithWallet();
+ return !IsPayingWithWallet() && IsManuallyEditingAnySection();
}
bool AutofillDialogControllerImpl::AutocheckoutIsRunning() const {
@@ -851,6 +853,9 @@ void AutofillDialogControllerImpl::EditClickedForSection(
model->FillInputs(inputs);
section_editing_state_[section] = true;
view_->UpdateSection(section, CLEAR_USER_INPUT);
+
+ GetMetricLogger().LogDialogUiEvent(
+ dialog_type_, DialogSectionToUiEditEvent(section));
}
void AutofillDialogControllerImpl::EditCancelledForSection(
@@ -910,7 +915,7 @@ gfx::Image AutofillDialogControllerImpl::IconForField(
}
bool AutofillDialogControllerImpl::InputIsValid(AutofillFieldType type,
- const string16& value) {
+ const string16& value) const {
switch (type) {
case EMAIL_ADDRESS:
return IsValidEmailAddress(value);
@@ -951,7 +956,7 @@ bool AutofillDialogControllerImpl::InputIsValid(AutofillFieldType type,
}
std::vector<AutofillFieldType> AutofillDialogControllerImpl::InputsAreValid(
- const DetailOutputMap& inputs, ValidationType validation_type) {
+ const DetailOutputMap& inputs, ValidationType validation_type) const {
std::vector<AutofillFieldType> invalid_fields;
std::map<AutofillFieldType, string16> field_values;
for (DetailOutputMap::const_iterator iter = inputs.begin();
@@ -1165,6 +1170,9 @@ void AutofillDialogControllerImpl::StartSignInFlow() {
content::Source<content::NavigationController> source(view_->ShowSignIn());
registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, source);
+
+ GetMetricLogger().LogDialogUiEvent(
+ dialog_type_, AutofillMetrics::DIALOG_UI_SIGNIN_SHOWN);
}
void AutofillDialogControllerImpl::EndSignInFlow() {
@@ -1213,10 +1221,7 @@ void AutofillDialogControllerImpl::OnCancel() {
if (callback_.is_null())
return;
- GetMetricLogger().LogDialogUiDuration(
- base::Time::Now() - dialog_shown_timestamp_,
- dialog_type_,
- AutofillMetrics::DIALOG_CANCELED);
+ LogOnCancelMetrics();
callback_.Run(NULL, std::string());
callback_ = base::Callback<void(const FormStructure*, const std::string&)>();
@@ -1328,6 +1333,8 @@ void AutofillDialogControllerImpl::SuggestionItemSelected(
const SuggestionsMenuModel& model) {
const DialogSection section = SectionForSuggestionsMenuModel(model);
EditCancelledForSection(section);
+
+ LogSuggestionItemSelectedMetric(model);
}
////////////////////////////////////////////////////////////////////////////////
@@ -1557,13 +1564,16 @@ AutofillDialogControllerImpl::AutofillDialogControllerImpl(
const std::string&)>& callback)
: profile_(Profile::FromBrowserContext(contents->GetBrowserContext())),
contents_(contents),
+ initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN),
+ dialog_type_(dialog_type),
form_structure_(form_structure, std::string()),
invoked_from_same_origin_(true),
source_url_(source_url),
ssl_status_(form_structure.ssl_status),
callback_(callback),
ALLOW_THIS_IN_INITIALIZER_LIST(
- account_chooser_model_(this, profile_->GetPrefs())),
+ account_chooser_model_(this, profile_->GetPrefs(), metric_logger_,
+ dialog_type)),
ALLOW_THIS_IN_INITIALIZER_LIST(
wallet_client_(profile_->GetRequestContext(), this)),
ALLOW_THIS_IN_INITIALIZER_LIST(suggested_email_(this)),
@@ -1573,8 +1583,6 @@ AutofillDialogControllerImpl::AutofillDialogControllerImpl(
ALLOW_THIS_IN_INITIALIZER_LIST(suggested_shipping_(this)),
input_showing_popup_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
- initial_user_state_(AutofillMetrics::DIALOG_USER_STATE_UNKNOWN),
- dialog_type_(dialog_type),
is_first_run_(!profile_->GetPrefs()->HasPrefPath(
::prefs::kAutofillDialogPayWithoutWallet)),
is_submitting_(false),
@@ -1801,6 +1809,14 @@ void AutofillDialogControllerImpl::SetCvcResult(const string16& cvc) {
SuggestionsMenuModel* AutofillDialogControllerImpl::
SuggestionsMenuModelForSection(DialogSection section) {
+ const AutofillDialogControllerImpl* const_this =
+ static_cast<const AutofillDialogControllerImpl*>(this);
+ return const_cast<SuggestionsMenuModel*>(
+ const_this->SuggestionsMenuModelForSection(section));
+}
+
+const SuggestionsMenuModel* AutofillDialogControllerImpl::
+ SuggestionsMenuModelForSection(DialogSection section) const {
switch (section) {
case SECTION_EMAIL:
return &suggested_email_;
@@ -1882,12 +1898,40 @@ void AutofillDialogControllerImpl::OnDidLoadRiskFingerprintData(
}
bool AutofillDialogControllerImpl::IsManuallyEditingSection(
- DialogSection section) {
- return section_editing_state_[section] ||
+ DialogSection section) const {
+ std::map<DialogSection, bool>::const_iterator it =
+ section_editing_state_.find(section);
+ return (it != section_editing_state_.end() && it->second) ||
SuggestionsMenuModelForSection(section)->
GetItemKeyForCheckedItem().empty();
}
+bool AutofillDialogControllerImpl::IsManuallyEditingAnySection() const {
+ for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) {
+ if (IsManuallyEditingSection(static_cast<DialogSection>(section)))
+ return true;
+ }
+ return false;
+}
+
+bool AutofillDialogControllerImpl::AllSectionsAreValid() const {
+ for (size_t section = SECTION_MIN; section <= SECTION_MAX; ++section) {
+ if (!SectionIsValid(static_cast<DialogSection>(section)))
+ return false;
+ }
+ return true;
+}
+
+bool AutofillDialogControllerImpl::SectionIsValid(
+ DialogSection section) const {
+ if (!IsManuallyEditingSection(section))
+ return true;
+
+ DetailOutputMap detail_outputs;
+ view_->GetUserInput(SECTION_EMAIL, &detail_outputs);
+ return InputsAreValid(detail_outputs, VALIDATE_EDIT).empty();
+}
+
bool AutofillDialogControllerImpl::ShouldUseBillingForShipping() {
// If the user is editing or inputting data, ask the view.
if (IsManuallyEditingSection(SECTION_SHIPPING))
@@ -1902,7 +1946,9 @@ bool AutofillDialogControllerImpl::ShouldSaveDetailsLocally() {
// It's possible that the user checked [X] Save details locally before
// switching payment methods, so only ask the view whether to save details
// locally if that checkbox is showing (currently if not paying with wallet).
- return !IsPayingWithWallet() && view_->SaveDetailsLocally();
+ // Also, if the user isn't editing any sections, there's no data to save
+ // locally.
+ return ShouldOfferToSaveInChrome() && view_->SaveDetailsLocally();
}
void AutofillDialogControllerImpl::SetIsSubmitting(bool submitting) {
@@ -2038,10 +2084,7 @@ void AutofillDialogControllerImpl::FinishSubmit() {
wallet_items_->google_transaction_id());
callback_ = base::Callback<void(const FormStructure*, const std::string&)>();
- GetMetricLogger().LogDialogUiDuration(
- base::Time::Now() - dialog_shown_timestamp_,
- dialog_type_,
- AutofillMetrics::DIALOG_ACCEPTED);
+ LogOnFinishSubmitMetrics();
switch (dialog_type_) {
case DIALOG_TYPE_AUTOCHECKOUT:
@@ -2060,6 +2103,65 @@ void AutofillDialogControllerImpl::FinishSubmit() {
}
}
+void AutofillDialogControllerImpl::LogOnFinishSubmitMetrics() {
+ GetMetricLogger().LogDialogUiDuration(
+ base::Time::Now() - dialog_shown_timestamp_,
+ dialog_type_,
+ AutofillMetrics::DIALOG_ACCEPTED);
+
+ GetMetricLogger().LogDialogUiEvent(
+ dialog_type_, AutofillMetrics::DIALOG_UI_ACCEPTED);
+
+ AutofillMetrics::DialogDismissalState dismissal_state;
+ if (!IsManuallyEditingAnySection())
+ dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_EXISTING_DATA;
+ else if (IsPayingWithWallet())
+ dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_SAVE_TO_WALLET;
+ else if (ShouldSaveDetailsLocally())
+ dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_SAVE_TO_AUTOFILL;
+ else
+ dismissal_state = AutofillMetrics::DIALOG_ACCEPTED_NO_SAVE;
+
+ GetMetricLogger().LogDialogDismissalState(dialog_type_, dismissal_state);
+}
+
+void AutofillDialogControllerImpl::LogOnCancelMetrics() {
+ GetMetricLogger().LogDialogUiEvent(
+ dialog_type_, AutofillMetrics::DIALOG_UI_CANCELED);
+
+ AutofillMetrics::DialogDismissalState dismissal_state;
+ if (!IsManuallyEditingAnySection())
+ dismissal_state = AutofillMetrics::DIALOG_CANCELED_NO_EDITS;
+ else if (AllSectionsAreValid())
+ dismissal_state = AutofillMetrics::DIALOG_CANCELED_NO_INVALID_FIELDS;
+ else
+ dismissal_state = AutofillMetrics::DIALOG_CANCELED_WITH_INVALID_FIELDS;
+
+ GetMetricLogger().LogDialogDismissalState(dialog_type_, dismissal_state);
+
+ GetMetricLogger().LogDialogUiDuration(
+ base::Time::Now() - dialog_shown_timestamp_,
+ dialog_type_,
+ AutofillMetrics::DIALOG_CANCELED);
+}
+
+void AutofillDialogControllerImpl::LogSuggestionItemSelectedMetric(
+ const SuggestionsMenuModel& model) {
+ DialogSection section = SectionForSuggestionsMenuModel(model);
+
+ AutofillMetrics::DialogUiEvent dialog_ui_event;
+ if (model.GetItemKeyForCheckedItem().empty()) {
+ // Selected to add a new item.
+ dialog_ui_event = DialogSectionToUiItemAddedEvent(section);
+ } else {
+ // Selected an existing item.
+ DCHECK(!section_editing_state_[section]);
+ dialog_ui_event = DialogSectionToUiSelectionChangedEvent(section);
+ }
+
+ GetMetricLogger().LogDialogUiEvent(dialog_type_, dialog_ui_event);
+}
+
AutofillMetrics::DialogInitialUserStateMetric
AutofillDialogControllerImpl::GetInitialUserState() const {
// Consider a user to be an Autofill user if the user has any credit cards
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h
index 92bd711..adfaccb 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h
+++ b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h
@@ -124,10 +124,11 @@ class AutofillDialogControllerImpl : public AutofillDialogController,
virtual void EditCancelledForSection(DialogSection section) OVERRIDE;
virtual gfx::Image IconForField(AutofillFieldType type,
const string16& user_input) const OVERRIDE;
- virtual bool InputIsValid(AutofillFieldType type, const string16& value)
- OVERRIDE;
+ virtual bool InputIsValid(AutofillFieldType type,
+ const string16& value) const OVERRIDE;
virtual std::vector<AutofillFieldType> InputsAreValid(
- const DetailOutputMap& inputs, ValidationType validation_type) OVERRIDE;
+ const DetailOutputMap& inputs,
+ ValidationType validation_type) const OVERRIDE;
virtual void UserEditedOrActivatedInput(const DetailInput* input,
gfx::NativeView parent_view,
const gfx::Rect& content_bounds,
@@ -311,6 +312,8 @@ class AutofillDialogControllerImpl : public AutofillDialogController,
// Gets the SuggestionsMenuModel for |section|.
SuggestionsMenuModel* SuggestionsMenuModelForSection(DialogSection section);
+ const SuggestionsMenuModel* SuggestionsMenuModelForSection(
+ DialogSection section) const;
// And the reverse.
DialogSection SectionForSuggestionsMenuModel(
const SuggestionsMenuModel& model);
@@ -353,7 +356,18 @@ class AutofillDialogControllerImpl : public AutofillDialogController,
// Whether the user has chosen to enter all new data in |section|. This
// happens via choosing "Add a new X..." from a section's suggestion menu.
- bool IsManuallyEditingSection(DialogSection section);
+ bool IsManuallyEditingSection(DialogSection section) const;
+
+ // Whether the user has chosen to enter all new data in at least one section.
+ bool IsManuallyEditingAnySection() const;
+
+ // Whether all of the input fields currently showing in the dialog have valid
+ // contents.
+ bool AllSectionsAreValid() const;
+
+ // Whether all of the input fields currently showing in the given |section| of
+ // the dialog have valid contents.
+ bool SectionIsValid(DialogSection section) const;
// Whether the billing section should be used to fill in the shipping details.
bool ShouldUseBillingForShipping();
@@ -381,6 +395,20 @@ class AutofillDialogControllerImpl : public AutofillDialogController,
// in order to fill |form_structure_| and pass data back to the invoking page.
void FinishSubmit();
+ // Logs metrics when the dialog is submitted.
+ void LogOnFinishSubmitMetrics();
+
+ // Logs metrics when the dialog is canceled.
+ void LogOnCancelMetrics();
+
+ // Logs metrics when the edit ui is shown for the given |section|.
+ void LogEditUiShownMetric(DialogSection section);
+
+ // Logs metrics when a suggestion item from the given |model| is selected.
+ void LogSuggestionItemSelectedMetric(const SuggestionsMenuModel& model);
+
+ // Returns the metric corresponding to the user's initial state when
+ // interacting with this dialog.
AutofillMetrics::DialogInitialUserStateMetric GetInitialUserState() const;
// The |profile| for |contents_|.
@@ -389,6 +417,15 @@ class AutofillDialogControllerImpl : public AutofillDialogController,
// The WebContents where the Autofill action originated.
content::WebContents* const contents_;
+ // For logging UMA metrics.
+ const AutofillMetrics metric_logger_;
+ base::Time dialog_shown_timestamp_;
+ base::Time autocheckout_started_timestamp_;
+ AutofillMetrics::DialogInitialUserStateMetric initial_user_state_;
+
+ // Whether this is an Autocheckout or a requestAutocomplete dialog.
+ const DialogType dialog_type_;
+
FormStructure form_structure_;
// Whether the URL visible to the user when this dialog was requested to be
@@ -478,15 +515,6 @@ class AutofillDialogControllerImpl : public AutofillDialogController,
base::WeakPtrFactory<AutofillDialogControllerImpl> weak_ptr_factory_;
- // For logging UMA metrics.
- const AutofillMetrics metric_logger_;
- base::Time dialog_shown_timestamp_;
- base::Time autocheckout_started_timestamp_;
- AutofillMetrics::DialogInitialUserStateMetric initial_user_state_;
-
- // Whether this is an Autocheckout or a requestAutocomplete dialog.
- const DialogType dialog_type_;
-
// Whether this is the first time this profile has seen the Autofill dialog.
bool is_first_run_;
diff --git a/chrome/browser/ui/autofill/autofill_dialog_models.cc b/chrome/browser/ui/autofill/autofill_dialog_models.cc
index 37f358a..229fea3 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_models.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_models.cc
@@ -12,6 +12,7 @@
#include "base/utf_string_conversions.h"
#include "chrome/common/pref_names.h"
#include "components/autofill/browser/autofill_country.h"
+#include "components/autofill/browser/autofill_metrics.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
@@ -105,13 +106,17 @@ AccountChooserModelDelegate::~AccountChooserModelDelegate() {}
AccountChooserModel::AccountChooserModel(
AccountChooserModelDelegate* delegate,
- PrefService* prefs)
+ PrefService* prefs,
+ const AutofillMetrics& metric_logger,
+ DialogType dialog_type)
: ALLOW_THIS_IN_INITIALIZER_LIST(ui::SimpleMenuModel(this)),
account_delegate_(delegate),
checked_item_(
prefs->GetBoolean(::prefs::kAutofillDialogPayWithoutWallet) ?
kAutofillItemId : kWalletItemId),
- had_wallet_error_(false) {
+ had_wallet_error_(false),
+ metric_logger_(metric_logger),
+ dialog_type_(dialog_type) {
AddCheckItem(kWalletItemId,
l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_GOOGLE_WALLET));
SetIcon(
@@ -145,6 +150,20 @@ void AccountChooserModel::ExecuteCommand(int command_id, int event_flags) {
if (checked_item_ == command_id)
return;
+ // Log metrics.
+ AutofillMetrics::DialogUiEvent chooser_event;
+ if (command_id == kAutofillItemId) {
+ chooser_event =
+ AutofillMetrics::DIALOG_UI_ACCOUNT_CHOOSER_SWITCHED_TO_AUTOFILL;
+ } else if (checked_item_ == kAutofillItemId) {
+ chooser_event =
+ AutofillMetrics::DIALOG_UI_ACCOUNT_CHOOSER_SWITCHED_TO_WALLET;
+ } else {
+ chooser_event =
+ AutofillMetrics::DIALOG_UI_ACCOUNT_CHOOSER_SWITCHED_WALLET_ACCOUNT;
+ }
+ metric_logger_.LogDialogUiEvent(dialog_type_, chooser_event);
+
checked_item_ = command_id;
account_delegate_->AccountChoiceChanged();
}
diff --git a/chrome/browser/ui/autofill/autofill_dialog_models.h b/chrome/browser/ui/autofill/autofill_dialog_models.h
index 2020e5d..44ff005 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_models.h
+++ b/chrome/browser/ui/autofill/autofill_dialog_models.h
@@ -11,9 +11,11 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/string16.h"
+#include "components/autofill/browser/autofill_manager_delegate.h"
#include "ui/base/models/combobox_model.h"
#include "ui/base/models/simple_menu_model.h"
+class AutofillMetrics;
class PrefService;
namespace autofill {
@@ -110,7 +112,9 @@ class AccountChooserModel : public ui::SimpleMenuModel,
public ui::SimpleMenuModel::Delegate {
public:
AccountChooserModel(AccountChooserModelDelegate* delegate,
- PrefService* prefs);
+ PrefService* prefs,
+ const AutofillMetrics& metric_logger,
+ DialogType dialog_type);
virtual ~AccountChooserModel();
// ui::SimpleMenuModel::Delegate implementation.
@@ -148,6 +152,10 @@ class AccountChooserModel : public ui::SimpleMenuModel,
// open.
bool had_wallet_error_;
+ // For logging UMA metrics.
+ const AutofillMetrics& metric_logger_;
+ const DialogType dialog_type_;
+
DISALLOW_COPY_AND_ASSIGN(AccountChooserModel);
};
diff --git a/chrome/browser/ui/autofill/autofill_dialog_models_unittest.cc b/chrome/browser/ui/autofill/autofill_dialog_models_unittest.cc
index 36cdbcd..cd03020 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_models_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_models_unittest.cc
@@ -6,6 +6,7 @@
#include "chrome/browser/ui/autofill/autofill_dialog_models.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/browser/autofill_metrics.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -13,6 +14,19 @@ namespace autofill {
namespace {
+class TestAccountChooserModel : public AccountChooserModel {
+ public:
+ TestAccountChooserModel(AccountChooserModelDelegate* delegate,
+ PrefService* prefs,
+ const AutofillMetrics& metric_logger)
+ : AccountChooserModel(delegate, prefs, metric_logger,
+ DIALOG_TYPE_REQUEST_AUTOCOMPLETE) {}
+ virtual ~TestAccountChooserModel() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestAccountChooserModel);
+};
+
class MockAccountChooserModelDelegate : public AccountChooserModelDelegate {
public:
MockAccountChooserModelDelegate() {}
@@ -23,17 +37,20 @@ class MockAccountChooserModelDelegate : public AccountChooserModelDelegate {
class AccountChooserModelTest : public testing::Test {
public:
- AccountChooserModelTest() : model_(&delegate_, profile_.GetPrefs()) {}
+ AccountChooserModelTest()
+ : model_(&delegate_, profile_.GetPrefs(), metric_logger_) {}
virtual ~AccountChooserModelTest() {}
Profile* profile() { return &profile_; }
MockAccountChooserModelDelegate* delegate() { return &delegate_; }
- AccountChooserModel* model() { return &model_; }
+ TestAccountChooserModel* model() { return &model_; }
+ const AutofillMetrics& metric_logger() { return metric_logger_; }
private:
TestingProfile profile_;
MockAccountChooserModelDelegate delegate_;
- AccountChooserModel model_;
+ TestAccountChooserModel model_;
+ AutofillMetrics metric_logger_;
};
} // namespace
@@ -43,14 +60,16 @@ TEST_F(AccountChooserModelTest, ObeysPref) {
{
profile()->GetPrefs()->SetBoolean(
::prefs::kAutofillDialogPayWithoutWallet, false);
- AccountChooserModel model(delegate(), profile()->GetPrefs());
+ TestAccountChooserModel model(delegate(), profile()->GetPrefs(),
+ metric_logger());
EXPECT_TRUE(model.WalletIsSelected());
}
// When the user chose to "Pay without wallet", use Autofill.
{
profile()->GetPrefs()->SetBoolean(
::prefs::kAutofillDialogPayWithoutWallet, true);
- AccountChooserModel model(delegate(), profile()->GetPrefs());
+ TestAccountChooserModel model(delegate(), profile()->GetPrefs(),
+ metric_logger());
EXPECT_FALSE(model.WalletIsSelected());
}
}
diff --git a/chrome/browser/ui/autofill/autofill_dialog_types.cc b/chrome/browser/ui/autofill/autofill_dialog_types.cc
index 81902c1..23cf0ba 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_types.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_types.cc
@@ -80,4 +80,73 @@ SuggestionState::SuggestionState(const string16& text,
editable(editable) {}
SuggestionState::~SuggestionState() {}
+AutofillMetrics::DialogUiEvent DialogSectionToUiEditEvent(
+ DialogSection section) {
+ switch (section) {
+ case SECTION_EMAIL:
+ return AutofillMetrics::DIALOG_UI_EMAIL_EDIT_UI_SHOWN;
+
+ case SECTION_BILLING:
+ return AutofillMetrics::DIALOG_UI_BILLING_EDIT_UI_SHOWN;
+
+ case SECTION_CC_BILLING:
+ return AutofillMetrics::DIALOG_UI_CC_BILLING_EDIT_UI_SHOWN;
+
+ case SECTION_SHIPPING:
+ return AutofillMetrics::DIALOG_UI_SHIPPING_EDIT_UI_SHOWN;
+
+ case SECTION_CC:
+ return AutofillMetrics::DIALOG_UI_CC_EDIT_UI_SHOWN;
+ }
+
+ NOTREACHED();
+ return AutofillMetrics::NUM_DIALOG_UI_EVENTS;
+}
+
+AutofillMetrics::DialogUiEvent DialogSectionToUiItemAddedEvent(
+ DialogSection section) {
+ switch (section) {
+ case SECTION_EMAIL:
+ return AutofillMetrics::DIALOG_UI_EMAIL_ITEM_ADDED;
+
+ case SECTION_BILLING:
+ return AutofillMetrics::DIALOG_UI_BILLING_ITEM_ADDED;
+
+ case SECTION_CC_BILLING:
+ return AutofillMetrics::DIALOG_UI_CC_BILLING_ITEM_ADDED;
+
+ case SECTION_SHIPPING:
+ return AutofillMetrics::DIALOG_UI_SHIPPING_ITEM_ADDED;
+
+ case SECTION_CC:
+ return AutofillMetrics::DIALOG_UI_CC_ITEM_ADDED;
+ }
+
+ NOTREACHED();
+ return AutofillMetrics::NUM_DIALOG_UI_EVENTS;
+}
+
+AutofillMetrics::DialogUiEvent DialogSectionToUiSelectionChangedEvent(
+ DialogSection section) {
+ switch (section) {
+ case SECTION_EMAIL:
+ return AutofillMetrics::DIALOG_UI_EMAIL_SELECTED_SUGGESTION_CHANGED;
+
+ case SECTION_BILLING:
+ return AutofillMetrics::DIALOG_UI_BILLING_SELECTED_SUGGESTION_CHANGED;
+
+ case SECTION_CC_BILLING:
+ return AutofillMetrics::DIALOG_UI_CC_BILLING_SELECTED_SUGGESTION_CHANGED;
+
+ case SECTION_SHIPPING:
+ return AutofillMetrics::DIALOG_UI_SHIPPING_SELECTED_SUGGESTION_CHANGED;
+
+ case SECTION_CC:
+ return AutofillMetrics::DIALOG_UI_CC_SELECTED_SUGGESTION_CHANGED;
+ }
+
+ NOTREACHED();
+ return AutofillMetrics::NUM_DIALOG_UI_EVENTS;
+}
+
} // namespace autofill
diff --git a/chrome/browser/ui/autofill/autofill_dialog_types.h b/chrome/browser/ui/autofill/autofill_dialog_types.h
index c10d3f6..15a770e 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_types.h
+++ b/chrome/browser/ui/autofill/autofill_dialog_types.h
@@ -10,6 +10,7 @@
#include "base/callback_forward.h"
#include "base/string16.h"
+#include "components/autofill/browser/autofill_metrics.h"
#include "components/autofill/browser/field_types.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/image/image.h"
@@ -46,13 +47,19 @@ typedef base::Callback<bool(const DetailInput& input,
// Sections of the dialog --- all fields that may be shown to the user fit under
// one of these sections.
enum DialogSection {
- SECTION_EMAIL,
+ // Lower boundary value for looping over all sections.
+ SECTION_MIN,
+
+ SECTION_EMAIL = SECTION_MIN,
// The Autofill-backed dialog uses separate CC and billing sections.
SECTION_CC,
SECTION_BILLING,
// The wallet-backed dialog uses a combined CC and billing section.
SECTION_CC_BILLING,
SECTION_SHIPPING,
+
+ // Upper boundary value for looping over all sections.
+ SECTION_MAX = SECTION_SHIPPING
};
// Used by UpdateSection() to indicate what to do with data that the user has
@@ -140,6 +147,21 @@ struct SuggestionState {
typedef std::vector<DetailInput> DetailInputs;
typedef std::map<const DetailInput*, string16> DetailOutputMap;
+// Returns the AutofillMetrics::DIALOG_UI_*_EDIT_UI_SHOWN metric corresponding
+// to the |section|.
+AutofillMetrics::DialogUiEvent DialogSectionToUiEditEvent(
+ DialogSection section);
+
+// Returns the AutofillMetrics::DIALOG_UI_*_ITEM_ADDED metric corresponding
+// to the |section|.
+AutofillMetrics::DialogUiEvent DialogSectionToUiItemAddedEvent(
+ DialogSection section);
+
+// Returns the AutofillMetrics::DIALOG_UI_*_ITEM_ADDED metric corresponding
+// to the |section|.
+AutofillMetrics::DialogUiEvent DialogSectionToUiSelectionChangedEvent(
+ DialogSection section);
+
} // namespace autofill
#endif // CHROME_BROWSER_UI_AUTOFILL_AUTOFILL_DIALOG_TYPES_H_
diff --git a/chrome/browser/ui/views/autofill/autofill_dialog_views.cc b/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
index fa85bcb..e8a0717 100644
--- a/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
@@ -1211,7 +1211,7 @@ void AutofillDialogViews::UpdateDetailsGroupState(const DetailsGroup& group) {
// Show or hide the "Save in chrome" checkbox. If nothing is in editing mode,
// hide. If the controller tells us not to show it, likewise hide.
save_in_chrome_checkbox_->SetVisible(
- controller_->ShouldOfferToSaveInChrome() && AtLeastOneSectionIsEditing());
+ controller_->ShouldOfferToSaveInChrome());
const bool has_suggestions =
controller_->MenuModelForSection(group.section)->GetItemCount() > 0;
@@ -1227,16 +1227,6 @@ void AutofillDialogViews::UpdateDetailsGroupState(const DetailsGroup& group) {
ContentsPreferredSizeChanged();
}
-bool AutofillDialogViews::AtLeastOneSectionIsEditing() {
- for (DetailGroupMap::iterator iter = detail_groups_.begin();
- iter != detail_groups_.end(); ++iter) {
- if (iter->second.manual_input && iter->second.manual_input->visible())
- return true;
- }
-
- return false;
-}
-
bool AutofillDialogViews::ValidateGroup(
DetailsGroup* group,
AutofillDialogController::ValidationType validation_type) {
diff --git a/chrome/browser/ui/views/autofill/autofill_dialog_views.h b/chrome/browser/ui/views/autofill/autofill_dialog_views.h
index 331ddc9..93e16ec 100644
--- a/chrome/browser/ui/views/autofill/autofill_dialog_views.h
+++ b/chrome/browser/ui/views/autofill/autofill_dialog_views.h
@@ -399,10 +399,6 @@ class AutofillDialogViews : public AutofillDialogView,
// Updates the visual state of the given group as per the model.
void UpdateDetailsGroupState(const DetailsGroup& group);
- // Returns true if at least one of the details sections is in manual input
- // mode.
- bool AtLeastOneSectionIsEditing();
-
// Gets a pointer to the DetailsGroup that's associated with the given section
// of the dialog.
DetailsGroup* GroupForSection(DialogSection section);
diff --git a/components/autofill/browser/autofill_metrics.cc b/components/autofill/browser/autofill_metrics.cc
index e83ae02..f36d3b1 100644
--- a/components/autofill/browser/autofill_metrics.cc
+++ b/components/autofill/browser/autofill_metrics.cc
@@ -353,6 +353,13 @@ void AutofillMetrics::LogCreditCardInfoBarMetric(InfoBarMetric metric) const {
NUM_INFO_BAR_METRICS);
}
+void AutofillMetrics::LogDialogDismissalState(
+ autofill::DialogType dialog_type,
+ DialogDismissalState state) const {
+ std::string name = GetPrefixForDialogType(dialog_type) + ".DismissalState";
+ LogUMAHistogramEnumeration(name, state, NUM_DIALOG_DISMISSAL_STATES);
+}
+
void AutofillMetrics::LogDialogInitialUserState(
autofill::DialogType dialog_type,
DialogInitialUserStateMetric user_type) const {
@@ -395,6 +402,12 @@ void AutofillMetrics::LogDialogUiDuration(
LogUMAHistogramLongTimes(prefix + ".UiDuration." + suffix, duration);
}
+void AutofillMetrics::LogDialogUiEvent(autofill::DialogType dialog_type,
+ DialogUiEvent event) const {
+ std::string name = GetPrefixForDialogType(dialog_type) + ".UiEvents";
+ LogUMAHistogramEnumeration(name, event, NUM_DIALOG_UI_EVENTS);
+}
+
void AutofillMetrics::LogWalletErrorMetric(autofill::DialogType dialog_type,
WalletErrorMetric metric) const {
std::string name = GetPrefixForDialogType(dialog_type) + ".WalletErrors";
diff --git a/components/autofill/browser/autofill_metrics.h b/components/autofill/browser/autofill_metrics.h
index ddd16b9..2f9abd0 100644
--- a/components/autofill/browser/autofill_metrics.h
+++ b/components/autofill/browser/autofill_metrics.h
@@ -67,12 +67,31 @@ class AutofillMetrics {
// The action the user took to dismiss a dialog.
enum DialogDismissalAction {
- DIALOG_ACCEPTED = 0, // The user accepted, i.e. confirmed, the dialog.
+ DIALOG_ACCEPTED = 0, // The user accepted, i.e. submitted, the dialog.
DIALOG_CANCELED, // The user canceled out of the dialog.
};
- // The initial state of user that's interacting with a freshly shown
- // requestAutocomplete or Autocheckout dialog.
+ // The state of the Autofill dialog when it was dismissed.
+ enum DialogDismissalState {
+ // The user submitted with no data available to save.
+ DIALOG_ACCEPTED_EXISTING_DATA,
+ // The saved details to Online Wallet on submit.
+ DIALOG_ACCEPTED_SAVE_TO_WALLET,
+ // The saved details to the local Autofill database on submit.
+ DIALOG_ACCEPTED_SAVE_TO_AUTOFILL,
+ // The user submitted without saving any edited sections.
+ DIALOG_ACCEPTED_NO_SAVE,
+ // The user canceled with no edit UI showing.
+ DIALOG_CANCELED_NO_EDITS,
+ // The user canceled with edit UI showing, but no invalid fields.
+ DIALOG_CANCELED_NO_INVALID_FIELDS,
+ // The user canceled with at least one invalid field.
+ DIALOG_CANCELED_WITH_INVALID_FIELDS,
+ NUM_DIALOG_DISMISSAL_STATES
+ };
+
+ // The initial state of user that's interacting with a freshly shown Autofill
+ // dialog.
enum DialogInitialUserStateMetric {
// Could not determine the user's state due to failure to communicate with
// the Wallet server.
@@ -115,6 +134,50 @@ class AutofillMetrics {
NUM_DIALOG_SECURITY_METRICS
};
+ // For measuring how users are interacting with the Autofill dialog UI.
+ enum DialogUiEvent {
+ // Baseline metric: The dialog was shown.
+ DIALOG_UI_SHOWN = 0,
+
+ // Dialog dismissal actions:
+ DIALOG_UI_ACCEPTED,
+ DIALOG_UI_CANCELED,
+
+ // Selections within the account switcher:
+ // Switched from a Wallet account to local Autofill data.
+ DIALOG_UI_ACCOUNT_CHOOSER_SWITCHED_TO_AUTOFILL,
+ // Switched from local Autofill data to a Wallet account.
+ DIALOG_UI_ACCOUNT_CHOOSER_SWITCHED_TO_WALLET,
+ // Switched from one Wallet account to another one.
+ DIALOG_UI_ACCOUNT_CHOOSER_SWITCHED_WALLET_ACCOUNT,
+
+ // The sign-in UI was shown.
+ DIALOG_UI_SIGNIN_SHOWN,
+
+ // Selecting a different item from a suggestion menu dropdown:
+ DIALOG_UI_EMAIL_SELECTED_SUGGESTION_CHANGED,
+ DIALOG_UI_BILLING_SELECTED_SUGGESTION_CHANGED,
+ DIALOG_UI_CC_BILLING_SELECTED_SUGGESTION_CHANGED,
+ DIALOG_UI_SHIPPING_SELECTED_SUGGESTION_CHANGED,
+ DIALOG_UI_CC_SELECTED_SUGGESTION_CHANGED,
+
+ // Showing the editing UI for a section of the dialog:
+ DIALOG_UI_EMAIL_EDIT_UI_SHOWN,
+ DIALOG_UI_BILLING_EDIT_UI_SHOWN,
+ DIALOG_UI_CC_BILLING_EDIT_UI_SHOWN,
+ DIALOG_UI_SHIPPING_EDIT_UI_SHOWN,
+ DIALOG_UI_CC_EDIT_UI_SHOWN,
+
+ // Adding a new item in a section of the dialog:
+ DIALOG_UI_EMAIL_ITEM_ADDED,
+ DIALOG_UI_BILLING_ITEM_ADDED,
+ DIALOG_UI_CC_BILLING_ITEM_ADDED,
+ DIALOG_UI_SHIPPING_ITEM_ADDED,
+ DIALOG_UI_CC_ITEM_ADDED,
+
+ NUM_DIALOG_UI_EVENTS
+ };
+
enum InfoBarMetric {
INFOBAR_SHOWN = 0, // We showed an infobar, e.g. prompting to save credit
// card info.
@@ -321,6 +384,10 @@ class AutofillMetrics {
virtual void LogUserHappinessMetric(UserHappinessMetric metric) const;
+ // Logs |state| to the dismissal states histogram for |dialog_type|.
+ virtual void LogDialogDismissalState(autofill::DialogType dialog_type,
+ DialogDismissalState state) const;
+
// This should be called as soon as the user's signed-in status and Wallet
// item count is known. Records that a user starting out in |user_state| is
// interacting with a dialog of |dialog_type|.
@@ -336,16 +403,20 @@ class AutofillMetrics {
virtual void LogDialogSecurityMetric(autofill::DialogType dialog_type,
DialogSecurityMetric metric) const;
- // This should be called when the requestAutocomplete dialog, invoked by a
- // dialog of type |dialog_type|, is closed. |duration| should be the time
- // elapsed between the dialog being shown and it being closed.
- // |dismissal_action| should indicate whether the user dismissed the dialog by
- // submitting the form data or by canceling.
+ // This should be called when the Autofill dialog, invoked by a dialog of type
+ // |dialog_type|, is closed. |duration| should be the time elapsed between
+ // the dialog being shown and it being closed. |dismissal_action| should
+ // indicate whether the user dismissed the dialog by submitting the form data
+ // or by canceling.
virtual void LogDialogUiDuration(
const base::TimeDelta& duration,
autofill::DialogType dialog_type,
DialogDismissalAction dismissal_action) const;
+ // Logs |event| to the UI events histogram for |dialog_type|.
+ virtual void LogDialogUiEvent(autofill::DialogType dialog_type,
+ DialogUiEvent event) const;
+
// Logs |metric| to the Wallet errors histogram for |dialog_type|.
virtual void LogWalletErrorMetric(autofill::DialogType dialog_type,
WalletErrorMetric metric) const;
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 64c1e2d..06536b8 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -68,6 +68,11 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="Autocheckout.DismissalState"
+ enum="AutofillDialogDismissalState">
+ <summary>The state of the Autocheckout dialog when it was dismissed.</summary>
+</histogram>
+
<histogram name="Autocheckout.FlowDuration" units="ms">
<summary>
Measures the time elapsed between when the user submitted the Autocheckout
@@ -133,6 +138,12 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="Autocheckout.UiEvents" enum="AutofillDialogUiEvents">
+ <summary>
+ Measures how users are interacting with the Autocheckout dialog UI.
+ </summary>
+</histogram>
+
<histogram name="Autocheckout.WalletErrors" enum="WalletErrors">
<summary>
Measures the frequency of errors in communicating with the Google Online
@@ -2831,6 +2842,13 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="RequestAutocomplete.DismissalState"
+ enum="AutofillDialogDismissalState">
+ <summary>
+ The state of the requestAutocomplete() dialog when it was dismissed.
+ </summary>
+</histogram>
+
<histogram name="RequestAutocomplete.InitialUserState"
enum="AutofillDialogInitialUserState">
<summary>
@@ -2874,6 +2892,12 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="RequestAutocomplete.UiEvents" enum="AutofillDialogUiEvents">
+ <summary>
+ Measures how users are interacting with the requestAutocomplete() dialog UI.
+ </summary>
+</histogram>
+
<histogram name="RequestAutocomplete.WalletErrors" enum="WalletErrors">
<summary>
Measures the frequency of errors in communicating with the Google Online
@@ -3087,6 +3111,16 @@ other types of suffix sets.
<int value="1" label="Includes type hints"/>
</enum>
+<enum name="AutofillDialogDismissalState" type="int">
+ <int value="0" label="Submitted, existing data"/>
+ <int value="1" label="Submitted, saved to Wallet"/>
+ <int value="2" label="Submitted, saved locally"/>
+ <int value="3" label="Submitted, no save"/>
+ <int value="4" label="Canceled, no edits"/>
+ <int value="5" label="Canceled, no invalid fields"/>
+ <int value="6" label="Canceled, 1+ invalid fields"/>
+</enum>
+
<enum name="AutofillDialogInitialUserState" type="int">
<int value="0" label="Not signed in, no Autofill"/>
<int value="1" label="Not signed in, has Autofill"/>
@@ -3107,6 +3141,31 @@ other types of suffix sets.
<int value="2" label="Cross-origin frame"/>
</enum>
+<enum name="AutofillDialogUiEvents" type="int">
+ <int value="0" label="Dialog shown"/>
+ <int value="1" label="Dialog submitted"/>
+ <int value="2" label="Dialog canceled"/>
+ <int value="3" label="Account switched: Wallet-&gt;Autofill"/>
+ <int value="4" label="Account switched: Autofill-&gt;Wallet"/>
+ <int value="5" label="Account switched: Wallet-&gt;Wallet"/>
+ <int value="6" label="Sign-in UI shown"/>
+ <int value="7" label="Selected different email suggestion"/>
+ <int value="8" label="Selected different billing suggestion"/>
+ <int value="9" label="Selected different cc+billing suggestion"/>
+ <int value="10" label="Selected different shipping suggestion"/>
+ <int value="11" label="Selected different cc suggestion"/>
+ <int value="12" label="Showed edit UI for email"/>
+ <int value="13" label="Showed edit UI for billing"/>
+ <int value="14" label="Showed edit UI for cc+billing"/>
+ <int value="15" label="Showed edit UI for shipping"/>
+ <int value="16" label="Showed edit UI for cc"/>
+ <int value="17" label="Selected 'Add email' suggestion"/>
+ <int value="18" label="Selected 'Add billing' suggestion"/>
+ <int value="19" label="Selected 'Add cc+billing' suggestion"/>
+ <int value="20" label="Selected 'Add shipping' suggestion"/>
+ <int value="21" label="Selected 'Add cc' suggestion"/>
+</enum>
+
<enum name="AutofillExperimentId" type="int">
<int value="0" label="No Experiment"/>
<int value="1" label="Unknown"/>