Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(485)

Side by Side Diff: chrome/browser/ui/passwords/password_bubble_experiment.cc

Issue 711043002: Finch experiment for limiting the password bubble annoyance. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: nits Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/passwords/password_bubble_experiment.h"
6
7 #include "base/metrics/field_trial.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/rand_util.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/time/time.h"
12 #include "chrome/common/pref_names.h"
13 #include "components/pref_registry/pref_registry_syncable.h"
14 #include "components/variations/variations_associated_data.h"
15
16 namespace password_bubble_experiment {
17 namespace {
18
19 bool IsNegativeEvent(password_manager::metrics_util::UIDismissalReason reason) {
20 return (reason == password_manager::metrics_util::NO_DIRECT_INTERACTION ||
21 reason == password_manager::metrics_util::CLICKED_NOPE ||
22 reason == password_manager::metrics_util::CLICKED_NEVER);
23 }
24
25 // "TimeSpan" experiment -----------------------------------------------------
26
27 bool ExtractTimeSpanParams(base::TimeDelta* time_delta, int* nopes_limit) {
28 std::map<std::string, std::string> params;
29 bool retrieved = variations::GetVariationParams(kExperimentName, &params);
30 if (!retrieved)
31 return false;
32 int days = 0;
33 if (!base::StringToInt(params[kParamTimeSpan], &days) ||
34 !base::StringToInt(params[kParamTimeSpanNopeThreshold], nopes_limit))
35 return false;
36 *time_delta = base::TimeDelta::FromDays(days);
37 return true;
38 }
39
40 bool OverwriteTimeSpanPrefsIfNeeded(PrefService* prefs,
41 base::TimeDelta time_span) {
42 base::Time beginning = base::Time::FromInternalValue(
43 prefs->GetInt64(prefs::kPasswordBubbleTimeStamp));
44 base::Time now = base::Time::Now();
45 if (beginning + time_span < now) {
46 prefs->SetInt64(prefs::kPasswordBubbleTimeStamp, now.ToInternalValue());
47 prefs->SetInteger(prefs::kPasswordBubbleNopesCount, 0);
48 return true;
49 }
50 return false;
51 }
52
53 // If user dismisses the bubble >= kParamTimeSpanNopeThreshold times during
54 // kParamTimeSpan days then the bubble isn't shown until the end of this time
55 // span.
56 bool ShouldShowBubbleTimeSpanExperiment(PrefService* prefs) {
57 base::TimeDelta time_span;
58 int nopes_limit = 0;
59 if (!ExtractTimeSpanParams(&time_span, &nopes_limit)) {
60 VLOG(2) << "Can't read parameters for "
61 << kExperimentName << " experiment";
62 return true;
63 }
64 // Check if the new time span has started.
65 if (OverwriteTimeSpanPrefsIfNeeded(prefs, time_span))
66 return true;
67 int current_nopes = prefs->GetInteger(prefs::kPasswordBubbleNopesCount);
68 return current_nopes < nopes_limit;
69 }
70
71 // Increase the "Nope" counter in prefs and start a new time span if needed.
72 void UpdateTimeSpanPrefs(
73 PrefService* prefs,
74 password_manager::metrics_util::UIDismissalReason reason) {
75 if (!IsNegativeEvent(reason))
76 return;
77 base::TimeDelta time_span;
78 int nopes_limit = 0;
79 if (!ExtractTimeSpanParams(&time_span, &nopes_limit)) {
80 VLOG(2) << "Can't read parameters for "
81 << kExperimentName << " experiment";
82 return;
83 }
84 OverwriteTimeSpanPrefsIfNeeded(prefs, time_span);
85 int current_nopes = prefs->GetInteger(prefs::kPasswordBubbleNopesCount);
86 prefs->SetInteger(prefs::kPasswordBubbleNopesCount, current_nopes + 1);
87 }
88
89 // "Probability" experiment --------------------------------------------------
90
91 bool ExtractProbabilityParams(unsigned* history_length, unsigned* saves) {
92 std::map<std::string, std::string> params;
93 bool retrieved = variations::GetVariationParams(kExperimentName, &params);
94 if (!retrieved)
95 return false;
96 return base::StringToUint(params[kParamProbabilityInteractionsCount],
97 history_length) &&
98 base::StringToUint(params[kParamProbabilityFakeSaves], saves);
99 }
100
101 std::vector<int> ReadInteractionHistory(PrefService* prefs) {
102 std::vector<int> interactions;
103 const base::ListValue* list =
104 prefs->GetList(prefs::kPasswordBubbleLastInteractions);
105 if (!list)
106 return interactions;
107 for (const base::Value* value : *list) {
108 int out_value;
109 if (value->GetAsInteger(&out_value))
110 interactions.push_back(out_value);
111 }
112 return interactions;
113 }
114
115 // We keep the history of last kParamProbabilityInteractionsCount interactions
116 // with the bubble. We implicitly add kParamProbabilityFakeSaves "Save" clicks.
117 // If there are x "Save" clicks among those kParamProbabilityInteractionsCount
118 // then the bubble is shown with probability (x + kParamProbabilityFakeSaves)/
119 // (kParamProbabilityInteractionsCount + kParamProbabilityFakeSaves).
120 bool ShouldShowBubbleProbabilityExperiment(PrefService* prefs) {
121 unsigned history_length = 0, fake_saves = 0;
122 if (!ExtractProbabilityParams(&history_length, &fake_saves)) {
123 VLOG(2) << "Can't read parameters for "
124 << kExperimentName << " experiment";
125 return true;
126 }
127 std::vector<int> interactions = ReadInteractionHistory(prefs);
128 unsigned real_saves =
129 std::count(interactions.begin(), interactions.end(),
130 password_manager::metrics_util::CLICKED_SAVE);
131 return (interactions.size() + fake_saves) * base::RandDouble() <=
132 real_saves + fake_saves;
133 }
134
135 void UpdateProbabilityPrefs(
136 PrefService* prefs,
137 password_manager::metrics_util::UIDismissalReason reason) {
138 if (!IsNegativeEvent(reason) &&
139 reason != password_manager::metrics_util::CLICKED_SAVE)
140 return;
141 unsigned history_length = 0, fake_saves = 0;
142 if (!ExtractProbabilityParams(&history_length, &fake_saves)) {
143 VLOG(2) << "Can't read parameters for "
144 << kExperimentName << " experiment";
145 return;
146 }
147 std::vector<int> interactions = ReadInteractionHistory(prefs);
148 interactions.push_back(reason);
149 size_t history_beginning = interactions.size() > history_length ?
150 interactions.size() - history_length : 0;
151 base::ListValue value;
152 for (size_t i = history_beginning; i < interactions.size(); ++i)
153 value.AppendInteger(interactions[i]);
154 prefs->Set(prefs::kPasswordBubbleLastInteractions, value);
155 }
156
157 } // namespace
158
159 const char kExperimentName[] = "PasswordBubbleAlgorithm";
160 const char kGroupTimeSpanBased[] = "TimeSpan";
161 const char kGroupProbabilityBased[] = "Probability";
162 const char kParamProbabilityFakeSaves[] = "saves_count";
163 const char kParamProbabilityInteractionsCount[] = "last_interactions_count";
164 const char kParamTimeSpan[] = "time_span";
165 const char kParamTimeSpanNopeThreshold[] = "nope_threshold";
166
167 void RegisterPrefs(user_prefs::PrefRegistrySyncable* registry) {
168 registry->RegisterInt64Pref(
169 prefs::kPasswordBubbleTimeStamp,
170 0,
171 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
172 registry->RegisterIntegerPref(
173 prefs::kPasswordBubbleNopesCount,
174 0,
175 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
176 registry->RegisterListPref(
177 prefs::kPasswordBubbleLastInteractions,
178 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
179 }
180
181 bool ShouldShowBubble(PrefService* prefs) {
182 if (!base::FieldTrialList::TrialExists(kExperimentName))
183 return true;
184 std::string group_name =
185 base::FieldTrialList::FindFullName(kExperimentName);
186
187 if (group_name == kGroupTimeSpanBased) {
188 return ShouldShowBubbleTimeSpanExperiment(prefs);
189 }
190 if (group_name == kGroupProbabilityBased) {
191 return ShouldShowBubbleProbabilityExperiment(prefs);
192 }
193
194 // The "Show Always" should be the default case.
195 return true;
196 }
197
198 void RecordBubbleClosed(
199 PrefService* prefs,
200 password_manager::metrics_util::UIDismissalReason reason) {
201 UpdateTimeSpanPrefs(prefs, reason);
202 UpdateProbabilityPrefs(prefs, reason);
203 }
204
205 } // namespace password_bubble_experiment
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698