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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/passwords/password_bubble_experiment.cc
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 0000000000000000000000000000000000000000..276a038e6a4e20cef8503e302217bc8bb390dbad
--- /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, &params);
+ 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, &params);
+ 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

Powered by Google App Engine
This is Rietveld 408576698