diff options
author | Vasilii Sukhanov <vasilii@chromium.org> | 2014-11-14 14:00:04 +0100 |
---|---|---|
committer | Vasilii Sukhanov <vasilii@chromium.org> | 2014-11-14 13:01:24 +0000 |
commit | 1fa6c42cee1471df2a3f3e383472267f3819497a (patch) | |
tree | c3623a7766603b03322b6048079eb479bda49a99 | |
parent | 120be7f3973f4acce516a993caf7d3ae74bc6ca7 (diff) | |
download | chromium_src-1fa6c42cee1471df2a3f3e383472267f3819497a.zip chromium_src-1fa6c42cee1471df2a3f3e383472267f3819497a.tar.gz chromium_src-1fa6c42cee1471df2a3f3e383472267f3819497a.tar.bz2 |
Finch experiment for limiting the password bubble annoyance.
BUG=431739
TBR=battre@chromium.org,vabr@chromium.org
Review URL: https://codereview.chromium.org/711043002
Cr-Commit-Position: refs/heads/master@{#303630}
(cherry picked from commit 4d7cdf425d0b57f4c2a766433f5ffdc3cedd909f)
Review URL: https://codereview.chromium.org/731473003
Cr-Commit-Position: refs/branch-heads/2214@{#41}
Cr-Branched-From: 03655fd3f6d72165dc3c9bd2c89807305316fe6c-refs/heads/master@{#303346}
-rw-r--r-- | chrome/browser/prefs/browser_prefs.cc | 2 | ||||
-rw-r--r-- | chrome/browser/ui/passwords/manage_passwords_bubble_model.cc | 16 | ||||
-rw-r--r-- | chrome/browser/ui/passwords/manage_passwords_ui_controller.cc | 5 | ||||
-rw-r--r-- | chrome/browser/ui/passwords/password_bubble_experiment.cc | 205 | ||||
-rw-r--r-- | chrome/browser/ui/passwords/password_bubble_experiment.h | 57 | ||||
-rw-r--r-- | chrome/browser/ui/passwords/password_bubble_experiment_unittest.cc | 139 | ||||
-rw-r--r-- | chrome/chrome_browser_ui.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests_unit.gypi | 1 | ||||
-rw-r--r-- | chrome/common/pref_names.cc | 10 | ||||
-rw-r--r-- | chrome/common/pref_names.h | 4 |
10 files changed, 441 insertions, 0 deletions
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 56bac85..6ea64ff 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -65,6 +65,7 @@ #include "chrome/browser/ui/browser_ui_prefs.h" #include "chrome/browser/ui/navigation_correction_tab_observer.h" #include "chrome/browser/ui/network_profile_bubble.h" +#include "chrome/browser/ui/passwords/password_bubble_experiment.h" #include "chrome/browser/ui/prefs/prefs_tab_helper.h" #include "chrome/browser/ui/search_engines/keyword_editor_controller.h" #include "chrome/browser/ui/startup/autolaunch_prompt.h" @@ -407,6 +408,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { MediaStreamDevicesController::RegisterProfilePrefs(registry); NetPrefObserver::RegisterProfilePrefs(registry); password_manager::PasswordManager::RegisterProfilePrefs(registry); + password_bubble_experiment::RegisterPrefs(registry); PrefProxyConfigTrackerImpl::RegisterProfilePrefs(registry); PrefsTabHelper::RegisterProfilePrefs(registry); Profile::RegisterProfilePrefs(registry); diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc index 1e1b433..dd59671 100644 --- a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc +++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc @@ -8,6 +8,7 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h" +#include "chrome/browser/ui/passwords/password_bubble_experiment.h" #include "chrome/grit/generated_resources.h" #include "components/password_manager/core/browser/password_store.h" #include "components/password_manager/core/common/password_manager_ui.h" @@ -33,6 +34,15 @@ int GetFieldWidth(FieldType type) { : kPasswordFieldSize); } +void RecordExperimentStatistics(content::WebContents* web_contents, + metrics_util::UIDismissalReason reason) { + if (!web_contents) + return; + Profile* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + password_bubble_experiment::RecordBubbleClosed(profile->GetPrefs(), reason); +} + } // namespace ManagePasswordsBubbleModel::ManagePasswordsBubbleModel( @@ -109,15 +119,20 @@ void ManagePasswordsBubbleModel::OnBubbleHidden() { return; metrics_util::LogUIDismissalReason(dismissal_reason_); + // Other use cases have been reported in the callbacks like OnSaveClicked(). + if (dismissal_reason_ == metrics_util::NO_DIRECT_INTERACTION) + RecordExperimentStatistics(web_contents(), dismissal_reason_); } void ManagePasswordsBubbleModel::OnNopeClicked() { dismissal_reason_ = metrics_util::CLICKED_NOPE; + RecordExperimentStatistics(web_contents(), dismissal_reason_); state_ = password_manager::ui::PENDING_PASSWORD_STATE; } void ManagePasswordsBubbleModel::OnNeverForThisSiteClicked() { dismissal_reason_ = metrics_util::CLICKED_NEVER; + RecordExperimentStatistics(web_contents(), dismissal_reason_); ManagePasswordsUIController* manage_passwords_ui_controller = ManagePasswordsUIController::FromWebContents(web_contents()); manage_passwords_ui_controller->NeverSavePassword(); @@ -134,6 +149,7 @@ void ManagePasswordsBubbleModel::OnUnblacklistClicked() { void ManagePasswordsBubbleModel::OnSaveClicked() { dismissal_reason_ = metrics_util::CLICKED_SAVE; + RecordExperimentStatistics(web_contents(), dismissal_reason_); ManagePasswordsUIController* manage_passwords_ui_controller = ManagePasswordsUIController::FromWebContents(web_contents()); manage_passwords_ui_controller->SavePassword(); diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc index 039f589..4afd320 100644 --- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc +++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc @@ -15,6 +15,7 @@ #include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/location_bar/location_bar.h" #include "chrome/browser/ui/passwords/manage_passwords_icon.h" +#include "chrome/browser/ui/passwords/password_bubble_experiment.h" #include "chrome/common/url_constants.h" #include "components/password_manager/core/browser/password_store.h" #include "content/public/browser/notification_service.h" @@ -268,6 +269,10 @@ void ManagePasswordsUIController::ShowBubbleWithoutUserInteraction() { Browser* browser = chrome::FindBrowserWithWebContents(web_contents()); if (!browser || browser->toolbar_model()->input_in_progress()) return; + if (state_ == password_manager::ui::PENDING_PASSWORD_AND_BUBBLE_STATE && + !password_bubble_experiment::ShouldShowBubble( + browser->profile()->GetPrefs())) + return; CommandUpdater* updater = browser->command_controller()->command_updater(); updater->ExecuteCommand(IDC_MANAGE_PASSWORDS_FOR_PAGE); #endif diff --git a/chrome/browser/ui/passwords/password_bubble_experiment.cc b/chrome/browser/ui/passwords/password_bubble_experiment.cc new file mode 100644 index 0000000..276a038 --- /dev/null +++ b/chrome/browser/ui/passwords/password_bubble_experiment.cc @@ -0,0 +1,205 @@ +// Copyright 2014 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/ui/passwords/password_bubble_experiment.h" + +#include "base/metrics/field_trial.h" +#include "base/prefs/pref_service.h" +#include "base/rand_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/time/time.h" +#include "chrome/common/pref_names.h" +#include "components/pref_registry/pref_registry_syncable.h" +#include "components/variations/variations_associated_data.h" + +namespace password_bubble_experiment { +namespace { + +bool IsNegativeEvent(password_manager::metrics_util::UIDismissalReason reason) { + return (reason == password_manager::metrics_util::NO_DIRECT_INTERACTION || + reason == password_manager::metrics_util::CLICKED_NOPE || + reason == password_manager::metrics_util::CLICKED_NEVER); +} + +// "TimeSpan" experiment ----------------------------------------------------- + +bool ExtractTimeSpanParams(base::TimeDelta* time_delta, int* nopes_limit) { + std::map<std::string, std::string> params; + bool retrieved = variations::GetVariationParams(kExperimentName, ¶ms); + if (!retrieved) + return false; + int days = 0; + if (!base::StringToInt(params[kParamTimeSpan], &days) || + !base::StringToInt(params[kParamTimeSpanNopeThreshold], nopes_limit)) + return false; + *time_delta = base::TimeDelta::FromDays(days); + return true; +} + +bool OverwriteTimeSpanPrefsIfNeeded(PrefService* prefs, + base::TimeDelta time_span) { + base::Time beginning = base::Time::FromInternalValue( + prefs->GetInt64(prefs::kPasswordBubbleTimeStamp)); + base::Time now = base::Time::Now(); + if (beginning + time_span < now) { + prefs->SetInt64(prefs::kPasswordBubbleTimeStamp, now.ToInternalValue()); + prefs->SetInteger(prefs::kPasswordBubbleNopesCount, 0); + return true; + } + return false; +} + +// If user dismisses the bubble >= kParamTimeSpanNopeThreshold times during +// kParamTimeSpan days then the bubble isn't shown until the end of this time +// span. +bool ShouldShowBubbleTimeSpanExperiment(PrefService* prefs) { + base::TimeDelta time_span; + int nopes_limit = 0; + if (!ExtractTimeSpanParams(&time_span, &nopes_limit)) { + VLOG(2) << "Can't read parameters for " + << kExperimentName << " experiment"; + return true; + } + // Check if the new time span has started. + if (OverwriteTimeSpanPrefsIfNeeded(prefs, time_span)) + return true; + int current_nopes = prefs->GetInteger(prefs::kPasswordBubbleNopesCount); + return current_nopes < nopes_limit; +} + +// Increase the "Nope" counter in prefs and start a new time span if needed. +void UpdateTimeSpanPrefs( + PrefService* prefs, + password_manager::metrics_util::UIDismissalReason reason) { + if (!IsNegativeEvent(reason)) + return; + base::TimeDelta time_span; + int nopes_limit = 0; + if (!ExtractTimeSpanParams(&time_span, &nopes_limit)) { + VLOG(2) << "Can't read parameters for " + << kExperimentName << " experiment"; + return; + } + OverwriteTimeSpanPrefsIfNeeded(prefs, time_span); + int current_nopes = prefs->GetInteger(prefs::kPasswordBubbleNopesCount); + prefs->SetInteger(prefs::kPasswordBubbleNopesCount, current_nopes + 1); +} + +// "Probability" experiment -------------------------------------------------- + +bool ExtractProbabilityParams(unsigned* history_length, unsigned* saves) { + std::map<std::string, std::string> params; + bool retrieved = variations::GetVariationParams(kExperimentName, ¶ms); + if (!retrieved) + return false; + return base::StringToUint(params[kParamProbabilityInteractionsCount], + history_length) && + base::StringToUint(params[kParamProbabilityFakeSaves], saves); +} + +std::vector<int> ReadInteractionHistory(PrefService* prefs) { + std::vector<int> interactions; + const base::ListValue* list = + prefs->GetList(prefs::kPasswordBubbleLastInteractions); + if (!list) + return interactions; + for (const base::Value* value : *list) { + int out_value; + if (value->GetAsInteger(&out_value)) + interactions.push_back(out_value); + } + return interactions; +} + +// We keep the history of last kParamProbabilityInteractionsCount interactions +// with the bubble. We implicitly add kParamProbabilityFakeSaves "Save" clicks. +// If there are x "Save" clicks among those kParamProbabilityInteractionsCount +// then the bubble is shown with probability (x + kParamProbabilityFakeSaves)/ +// (kParamProbabilityInteractionsCount + kParamProbabilityFakeSaves). +bool ShouldShowBubbleProbabilityExperiment(PrefService* prefs) { + unsigned history_length = 0, fake_saves = 0; + if (!ExtractProbabilityParams(&history_length, &fake_saves)) { + VLOG(2) << "Can't read parameters for " + << kExperimentName << " experiment"; + return true; + } + std::vector<int> interactions = ReadInteractionHistory(prefs); + unsigned real_saves = + std::count(interactions.begin(), interactions.end(), + password_manager::metrics_util::CLICKED_SAVE); + return (interactions.size() + fake_saves) * base::RandDouble() <= + real_saves + fake_saves; +} + +void UpdateProbabilityPrefs( + PrefService* prefs, + password_manager::metrics_util::UIDismissalReason reason) { + if (!IsNegativeEvent(reason) && + reason != password_manager::metrics_util::CLICKED_SAVE) + return; + unsigned history_length = 0, fake_saves = 0; + if (!ExtractProbabilityParams(&history_length, &fake_saves)) { + VLOG(2) << "Can't read parameters for " + << kExperimentName << " experiment"; + return; + } + std::vector<int> interactions = ReadInteractionHistory(prefs); + interactions.push_back(reason); + size_t history_beginning = interactions.size() > history_length ? + interactions.size() - history_length : 0; + base::ListValue value; + for (size_t i = history_beginning; i < interactions.size(); ++i) + value.AppendInteger(interactions[i]); + prefs->Set(prefs::kPasswordBubbleLastInteractions, value); +} + +} // namespace + +const char kExperimentName[] = "PasswordBubbleAlgorithm"; +const char kGroupTimeSpanBased[] = "TimeSpan"; +const char kGroupProbabilityBased[] = "Probability"; +const char kParamProbabilityFakeSaves[] = "saves_count"; +const char kParamProbabilityInteractionsCount[] = "last_interactions_count"; +const char kParamTimeSpan[] = "time_span"; +const char kParamTimeSpanNopeThreshold[] = "nope_threshold"; + +void RegisterPrefs(user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterInt64Pref( + prefs::kPasswordBubbleTimeStamp, + 0, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterIntegerPref( + prefs::kPasswordBubbleNopesCount, + 0, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref( + prefs::kPasswordBubbleLastInteractions, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); +} + +bool ShouldShowBubble(PrefService* prefs) { + if (!base::FieldTrialList::TrialExists(kExperimentName)) + return true; + std::string group_name = + base::FieldTrialList::FindFullName(kExperimentName); + + if (group_name == kGroupTimeSpanBased) { + return ShouldShowBubbleTimeSpanExperiment(prefs); + } + if (group_name == kGroupProbabilityBased) { + return ShouldShowBubbleProbabilityExperiment(prefs); + } + + // The "Show Always" should be the default case. + return true; +} + +void RecordBubbleClosed( + PrefService* prefs, + password_manager::metrics_util::UIDismissalReason reason) { + UpdateTimeSpanPrefs(prefs, reason); + UpdateProbabilityPrefs(prefs, reason); +} + +} // namespace password_bubble_experiment diff --git a/chrome/browser/ui/passwords/password_bubble_experiment.h b/chrome/browser/ui/passwords/password_bubble_experiment.h new file mode 100644 index 0000000..9c4d302 --- /dev/null +++ b/chrome/browser/ui/passwords/password_bubble_experiment.h @@ -0,0 +1,57 @@ +// Copyright 2014 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_UI_PASSWORDS_PASSWORD_BUBBLE_EXPERIMENT_H_ +#define CHROME_BROWSER_UI_PASSWORDS_PASSWORD_BUBBLE_EXPERIMENT_H_ + +#include "base/macros.h" +#include "components/password_manager/core/browser/password_manager_metrics_util.h" + +namespace user_prefs { +class PrefRegistrySyncable; +} + +class PrefService; + +// These functions handle the algorithms according to which the "Save password?" +// bubble is shown to user. +namespace password_bubble_experiment { + +void RegisterPrefs(user_prefs::PrefRegistrySyncable* registry); + +// The decision is made based on the "PasswordBubbleAlgorithm" finch experiment. +// The default value is true. +// It should be called before showing the "Save Password?" dialog. +bool ShouldShowBubble(PrefService* prefs); + +// Should be called when user dismisses the "Save Password?" dialog. It stores +// the statistics about interactions with the bubble. +void RecordBubbleClosed( + PrefService* prefs, + password_manager::metrics_util::UIDismissalReason reason); + +// The name of the finch experiment controlling the algorithm. +extern const char kExperimentName[]; + +// The group name for the time based algorithm. +extern const char kGroupTimeSpanBased[]; + +// The group name for the probability algorithm. +extern const char kGroupProbabilityBased[]; + +// For "Probability" group. The additional "Saves" to be added to the model. +extern const char kParamProbabilityFakeSaves[]; + +// For "Probability" group. The interaction history length. +extern const char kParamProbabilityInteractionsCount[]; + +// For "TimeSpan" group. The time span until the nope counter is zeroed. +extern const char kParamTimeSpan[]; + +// For "TimeSpan" group. The nopes threshold. +extern const char kParamTimeSpanNopeThreshold[]; + +} // namespace password_bubble_experiment + +#endif // CHROME_BROWSER_UI_PASSWORDS_PASSWORD_BUBBLE_EXPERIMENT_H_ diff --git a/chrome/browser/ui/passwords/password_bubble_experiment_unittest.cc b/chrome/browser/ui/passwords/password_bubble_experiment_unittest.cc new file mode 100644 index 0000000..96265b5 --- /dev/null +++ b/chrome/browser/ui/passwords/password_bubble_experiment_unittest.cc @@ -0,0 +1,139 @@ +// Copyright 2014 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/ui/passwords/password_bubble_experiment.h" + +#include "base/files/scoped_temp_dir.h" +#include "base/metrics/field_trial.h" +#include "base/prefs/pref_service.h" +#include "base/strings/string_number_conversions.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/base/testing_profile.h" +#include "components/variations/entropy_provider.h" +#include "components/variations/variations_associated_data.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const int kTimeSpanDays = 2; +const int kTimeSpanThreshold = 3; +const int kProbabilityFakeSaves = 0; +const int kProbabilityHistory = 10; + +void SetupTimeSpanExperiment() { + ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial( + password_bubble_experiment::kExperimentName, + password_bubble_experiment::kGroupTimeSpanBased)); + std::map<std::string, std::string> params; + params[password_bubble_experiment::kParamTimeSpan] = + base::IntToString(kTimeSpanDays); + params[password_bubble_experiment::kParamTimeSpanNopeThreshold] = + base::IntToString(kTimeSpanThreshold); + ASSERT_TRUE(variations::AssociateVariationParams( + password_bubble_experiment::kExperimentName, + password_bubble_experiment::kGroupTimeSpanBased, + params)); +} + +void SetupProbabilityExperiment() { + ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial( + password_bubble_experiment::kExperimentName, + password_bubble_experiment::kGroupProbabilityBased)); + std::map<std::string, std::string> params; + params[password_bubble_experiment::kParamProbabilityFakeSaves] = + base::IntToString(kProbabilityFakeSaves); + params[password_bubble_experiment::kParamProbabilityInteractionsCount] = + base::IntToString(kProbabilityHistory); + ASSERT_TRUE(variations::AssociateVariationParams( + password_bubble_experiment::kExperimentName, + password_bubble_experiment::kGroupProbabilityBased, + params)); +} + +} // namespace + +class PasswordBubbleExperimentTest : public testing::Test { + public: + void SetUp() override { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + profile_.reset(new TestingProfile(temp_dir_.path())); + + field_trial_list_.reset(new base::FieldTrialList( + new metrics::SHA1EntropyProvider("foo"))); + variations::testing::ClearAllVariationParams(); + } + + PrefService* prefs() { return profile_->GetPrefs(); } + + private: + base::ScopedTempDir temp_dir_; + scoped_ptr<TestingProfile> profile_; + scoped_ptr<base::FieldTrialList> field_trial_list_; +}; + +TEST_F(PasswordBubbleExperimentTest, TimeSpan) { + SetupTimeSpanExperiment(); + + EXPECT_TRUE(password_bubble_experiment::ShouldShowBubble(prefs())); + // Don't save password enough times. + for (int i = 0; i < kTimeSpanThreshold; ++i) { + password_manager::metrics_util::UIDismissalReason reason = i % 2 ? + password_manager::metrics_util::NO_DIRECT_INTERACTION : + password_manager::metrics_util::CLICKED_NOPE; + password_bubble_experiment::RecordBubbleClosed(prefs(), reason); + } + EXPECT_FALSE(password_bubble_experiment::ShouldShowBubble(prefs())); + + // Save password many times. It doesn't bring the bubble back while the time + // span isn't over. + for (int i = 0; i < 2*kTimeSpanThreshold; ++i) { + password_bubble_experiment::RecordBubbleClosed( + prefs(), + password_manager::metrics_util::CLICKED_SAVE); + } + EXPECT_FALSE(password_bubble_experiment::ShouldShowBubble(prefs())); +} + +TEST_F(PasswordBubbleExperimentTest, TimeSpanOver) { + SetupTimeSpanExperiment(); + + base::Time past_interval = + base::Time::Now() - base::TimeDelta::FromDays(kTimeSpanDays + 1); + prefs()->SetInt64(prefs::kPasswordBubbleTimeStamp, + past_interval.ToInternalValue()); + prefs()->SetInteger(prefs::kPasswordBubbleNopesCount, kTimeSpanThreshold); + // The time span is over. The bubble should be shown. + EXPECT_TRUE(password_bubble_experiment::ShouldShowBubble(prefs())); + EXPECT_EQ(0, prefs()->GetInteger(prefs::kPasswordBubbleNopesCount)); + + // Set the old time span again and record "Nope". The counter restarts from 0. + prefs()->SetInt64(prefs::kPasswordBubbleTimeStamp, + past_interval.ToInternalValue()); + password_bubble_experiment::RecordBubbleClosed( + prefs(), password_manager::metrics_util::CLICKED_NOPE); + EXPECT_TRUE(password_bubble_experiment::ShouldShowBubble(prefs())); + EXPECT_EQ(1, prefs()->GetInteger(prefs::kPasswordBubbleNopesCount)); +} + +TEST_F(PasswordBubbleExperimentTest, Probability) { + SetupProbabilityExperiment(); + + EXPECT_TRUE(password_bubble_experiment::ShouldShowBubble(prefs())); + // Don't save password enough times. + for (int i = 0; i < kProbabilityHistory; ++i) { + password_manager::metrics_util::UIDismissalReason reason = i % 2 ? + password_manager::metrics_util::NO_DIRECT_INTERACTION : + password_manager::metrics_util::CLICKED_NOPE; + password_bubble_experiment::RecordBubbleClosed(prefs(), reason); + } + EXPECT_FALSE(password_bubble_experiment::ShouldShowBubble(prefs())); + + // Save password enough times. + for (int i = 0; i < kProbabilityHistory; ++i) { + password_bubble_experiment::RecordBubbleClosed( + prefs(), + password_manager::metrics_util::CLICKED_SAVE); + } + EXPECT_TRUE(password_bubble_experiment::ShouldShowBubble(prefs())); +} diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi index c1730a2..81c6d34 100644 --- a/chrome/chrome_browser_ui.gypi +++ b/chrome/chrome_browser_ui.gypi @@ -804,6 +804,8 @@ 'browser/ui/passwords/manage_passwords_icon.h', 'browser/ui/passwords/manage_passwords_ui_controller.cc', 'browser/ui/passwords/manage_passwords_ui_controller.h', + 'browser/ui/passwords/password_bubble_experiment.cc', + 'browser/ui/passwords/password_bubble_experiment.h', 'browser/ui/passwords/password_manager_presenter.cc', 'browser/ui/passwords/password_manager_presenter.h', 'browser/ui/passwords/password_ui_view.h', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index f774021..a71b5f7 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -1145,6 +1145,7 @@ 'browser/ui/passwords/manage_passwords_bubble_model_unittest.cc', 'browser/ui/passwords/manage_passwords_icon_mock.cc', 'browser/ui/passwords/manage_passwords_ui_controller_unittest.cc', + 'browser/ui/passwords/password_bubble_experiment_unittest.cc', 'browser/ui/passwords/password_manager_presenter_unittest.cc', 'browser/ui/search/instant_page_unittest.cc', 'browser/ui/search/instant_search_prerenderer_unittest.cc', diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 2c2d7d4..fef4477 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -2279,4 +2279,14 @@ const char kBrowserAddPersonEnabled[] = "profile.add_person_enabled"; // A dictionary that maps user id to hardlock state. const char kEasyUnlockHardlockState[] = "easy_unlock.hardlock_state"; +// The beginning of time span when we count user's "Nope" for the password +// bubble. +const char kPasswordBubbleTimeStamp[] = "password_bubble.timestamp"; + +// The count of user's "Nope" for the password bubble. +const char kPasswordBubbleNopesCount[] = "password_bubble.nopes"; + +// Last user's interaction with the password bubble. +const char kPasswordBubbleLastInteractions[] = "password_bubble.interactions"; + } // namespace prefs diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 01a0089..214a3b5 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -805,6 +805,10 @@ extern const char kBrowserAddPersonEnabled[]; extern const char kEasyUnlockHardlockState[]; +extern const char kPasswordBubbleTimeStamp[]; +extern const char kPasswordBubbleNopesCount[]; +extern const char kPasswordBubbleLastInteractions[]; + } // namespace prefs #endif // CHROME_COMMON_PREF_NAMES_H_ |