1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
|
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_HATS_TRUST_SAFETY_SENTIMENT_SERVICE_H_
#define CHROME_BROWSER_UI_HATS_TRUST_SAFETY_SENTIMENT_SERVICE_H_
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_multi_source_observation.h"
#include "base/time/time.h"
#include "chrome/browser/download/download_item_warning_data.h"
#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_observer.h"
#include "components/browsing_data/core/browsing_data_utils.h"
#include "components/download/public/common/download_danger_type.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
#include "components/safe_browsing/core/browser/password_protection/metrics_util.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
using PasswordProtectionUIType = safe_browsing::WarningUIType;
using PasswordProtectionUIAction = safe_browsing::WarningAction;
inline constexpr base::TimeDelta kPasswordChangeInactivity = base::Minutes(30);
inline constexpr base::TimeDelta kSafetyHubSurveyDelay = base::Minutes(10);
// Service which receives events from Trust & Safety features and determines
// whether or not to launch a HaTS survey on the NTP for the user.
class TrustSafetySentimentService
: public KeyedService,
public ProfileObserver,
public metrics::DesktopSessionDurationTracker::Observer {
public:
explicit TrustSafetySentimentService(Profile* profile);
~TrustSafetySentimentService() override;
// Called when the user opens an NTP. This allows the service to update its
// eligibility logic, and potentially show a survey. Virtual to allow mocking
// in tests.
virtual void OpenedNewTabPage();
// Called when the user interacts with the privacy settings on
// chrome://settings in |web_contents|. Interaction in this context could be
// using a link row on the privacy settings card. Calling this allows the
// service to monitor |web_contents| to determine if the user stays on
// settings for the required time. Virtual to allow mocking in tests.
virtual void InteractedWithPrivacySettings(
content::WebContents* web_contents);
// Called when the user runs safety check. This is immediately considered as a
// trigger action. Virtual to allow mocking in tests.
virtual void RanSafetyCheck();
// Called when the user opens Page Info.
virtual void PageInfoOpened();
// Called when the user interacts in some way with Page Info.
virtual void InteractedWithPageInfo();
// Called when the user saves a password via the manage passwords UI. This is
// the native UI shown when Chrome detects a password has been entered into
// the web page.
virtual void SavedPassword();
// Called when the user closes Page Info. If Page Info was opened for the
// target time, or the user interacted with it while it was open, a trigger
// action is recorded.
virtual void PageInfoClosed();
// Called when the user visits chrome://settings/passwords. Calling this
// allows the service to monitor |web_contents| to determine if the user
// remains on settings after visiting the page for the required time. Virtual
// to allow mocking in tests.
virtual void OpenedPasswordManager(content::WebContents* web_contents);
// Called when the user saves a card through the native UI bubble shown after
// the user uses a card on a website.
virtual void SavedCard();
// Called when the user runs password check. Virtual to allow mocking in
// tests.
virtual void RanPasswordCheck();
// Called when the user deletes data from Clear Browsing Data dialog.
virtual void ClearedBrowsingData(browsing_data::BrowsingDataType datatype);
// Called when the user finishes the privacy guide. Virtual to allow mocking
// in tests.
virtual void FinishedPrivacyGuide();
// Profile Observer:
void OnOffTheRecordProfileCreated(Profile* off_the_record) override;
void OnProfileWillBeDestroyed(Profile* profile) override;
// metrics::DesktopSessionDurationTracker::Observer
void OnSessionEnded(base::TimeDelta session_length,
base::TimeTicks session_end) override;
// The feature areas that the service delivers HaTS surveys for. Each feature
// area is associated with a different Listnr survey, and has a different set
// of Product Specific Data (PSD). kIneligible is an exception, and
// is not associated with any survey, but rather represents the collection of
// features for which interaction with should also be considered when
// determining elibigility for a survey.
// These values are persisted to logs and entries should not be renumbered or
// reused and kept up to date with TrustSafetySentimentFeatureArea in
// enums.xml.
enum class FeatureArea {
kIneligible = 0,
kPrivacySettings = 1,
kTrustedSurface = 2,
kTransactions = 3,
// kPrivacySandbox3ConsentAccept = 4, // DEPRECATED.
// kPrivacySandbox3ConsentDecline = 5, // DEPRECATED.
// kPrivacySandbox3NoticeDismiss = 6, // DEPRECATED.
// kPrivacySandbox3NoticeOk = 7, // DEPRECATED.
// kPrivacySandbox3NoticeSettings = 8, // DEPRECATED.
// kPrivacySandbox3NoticeLearnMore = 9, // DEPRECATED.
kSafetyCheck = 10,
kPasswordCheck = 11,
kBrowsingData = 12,
kPrivacyGuide = 13,
kControlGroup = 14,
// kPrivacySandbox4ConsentAccept = 15, // DEPRECATED.
// kPrivacySandbox4ConsentDecline = 16, // DEPRECATED.
// kPrivacySandbox4NoticeOk = 17, // DEPRECATED.
// kPrivacySandbox4NoticeSettings = 18, // DEPRECATED.
kSafeBrowsingInterstitial = 19,
kDownloadWarningUI = 20,
kPasswordProtectionUI = 21,
kSafetyHubNotification = 22,
kSafetyHubInteracted = 23,
kMaxValue = kSafetyHubInteracted,
};
// Called when the user interacts with a safe browsing blocking page.
virtual void InteractedWithSafeBrowsingInterstitial(
bool did_proceed,
safe_browsing::SBThreatType threat_type);
// Called when the user completes terminal action within a download warning.
// These actions can include: DISCARD, and PROCEED.
virtual void InteractedWithDownloadWarningUI(
DownloadItemWarningData::WarningSurface surface,
DownloadItemWarningData::WarningAction action);
// Called when user clicks to protect/reset/check their password on a password
// protection UI. This triggers a survey if the user has not finished changing
// their password after a certain period of time.
virtual void ProtectResetOrCheckPasswordClicked(
PasswordProtectionUIType ui_type);
// Called when a user sees a password protection warning and decides to ignore
// the warning, close the warning, or mark the warning as legitimate.
virtual void PhishedPasswordUpdateNotClicked(
PasswordProtectionUIType ui_type,
PasswordProtectionUIAction action);
// Called when a user finishes updating their phished password after seeing a
// warning.
virtual void PhishedPasswordUpdateFinished();
// Checks that this feature area is valid for the current version.
static bool VersionCheck(FeatureArea feature_area);
// Gets the HaTS trigger for a feature area.
static std::string GetHatsTriggerForFeatureArea(FeatureArea feature_area);
// Performs a FeatureArea and Version-specific dice roll.
// Returns true if succeeds, else false.
static bool ProbabilityCheck(FeatureArea feature_area);
// Triggers a survey for Safety Hub for the given feature area (visiting SH or
// seeing a notification).
virtual void TriggerSafetyHubSurvey(
TrustSafetySentimentService::FeatureArea feature_area,
std::map<std::string, bool> product_specific_data);
private:
friend class TrustSafetySentimentServiceTest;
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest,
Eligibility_NtpOpens);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest, Eligibility_Time);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest, TriggerProbability);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest,
TriggersClearOnLaunch);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest,
SettingsWatcher_PrivacySettings);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest,
SettingsWatcher_PasswordManager);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest, RanSafetyCheck);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest,
PrivacySettingsProductSpecificData);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest,
Eligibility_V1FeatureWhileV2Enabled);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest, V2_SafetyCheck);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest, V2_TrustedSurface);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest, V2_PasswordCheck);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest, V2_BrowsingData);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest,
V2_BrowsingData_NotInterested);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest, V2_PrivacyGuide);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest, V2_ControlGroup);
FRIEND_TEST_ALL_PREFIXES(TrustSafetySentimentServiceTest,
SafetyHubInteractionState);
// Struct representing a trigger (user action relevant to T&S) that previously
// occurred, and is awaiting the appropriate eligibility steps before causing
// a survey to be shown.
struct PendingTrigger {
PendingTrigger();
PendingTrigger(const PendingTrigger& other);
PendingTrigger(const std::map<std::string, bool>& product_specific_data,
int remaining_ntps_to_open);
explicit PendingTrigger(int remaining_ntps_to_open);
~PendingTrigger();
std::map<std::string, bool> product_specific_data;
int remaining_ntps_to_open;
base::Time occurred_time;
};
// Class which observes the provided |web_contents| for |required_open_time|
// and then checks if |web_contents| is currently visible, and has settings
// open. Calls |success_callback| if the user stays on settings for the
// required time, calls |complete_callback| when the observation time has
// expired, or |web_contents| has been destroyed.
class SettingsWatcher : content::WebContentsObserver {
public:
SettingsWatcher(content::WebContents* web_contents,
base::TimeDelta required_open_time,
base::OnceCallback<void()> success_callback,
base::OnceCallback<void()> complete_callback);
~SettingsWatcher() override;
// WebContentsObserver:
void WebContentsDestroyed() override;
private:
void TimerComplete();
raw_ptr<content::WebContents> web_contents_;
base::OnceCallback<void()> success_callback_;
base::OnceCallback<void()> complete_callback_;
base::WeakPtrFactory<SettingsWatcher> weak_ptr_factory_{this};
};
// Struct which represents the PageInfo state of interest to the service.
struct PageInfoState {
PageInfoState();
base::Time opened_time;
bool interacted = false;
};
// Struct which represents the PhishedPasswordChange state. When a user clicks
// to change their password, we want to wait to trigger a survey until after
// they change their password or the user has been inactive for some time.
struct PhishedPasswordChangeState {
PhishedPasswordChangeState();
base::Time password_change_click_ts_;
PasswordProtectionUIType ui_type_;
bool finished_action = false;
};
void SettingsWatcherComplete();
// Record that a trigger occurred, placing it in the set of pending triggers.
// Private as the service itself determines when a trigger has occurred, and
// is responsible for generating the appropriate |product_specific_data|.
void TriggerOccurred(
FeatureArea feature_area,
const std::map<std::string, bool>& product_specific_data);
// Record that the user performed an action which should make them temporarily
// ineligible to receive a survey. This records a trigger for the kIneligible
// feature area, which just like any other trigger will prevent a survey from
// being shown, but will not result in a survey.
void PerformedIneligibleAction();
static bool ShouldBlockSurvey(const PendingTrigger& trigger);
// Called by |ProtectResetOrCheckPasswordClicked| and
// |PhishedPasswordUpdateNotClicked|. Triggers a survey if one has not already
// been triggered for the user journey.
void MaybeTriggerPasswordProtectionSurvey(PasswordProtectionUIType ui_type,
PasswordProtectionUIAction action);
const raw_ptr<Profile> profile_;
std::map<FeatureArea, PendingTrigger> pending_triggers_;
std::unique_ptr<SettingsWatcher> settings_watcher_;
std::unique_ptr<PageInfoState> page_info_state_;
std::unique_ptr<PhishedPasswordChangeState> phished_password_change_state_;
base::ScopedMultiSourceObservation<Profile, ProfileObserver>
observed_profiles_{this};
bool performed_control_group_dice_roll_;
base::WeakPtrFactory<TrustSafetySentimentService> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_UI_HATS_TRUST_SAFETY_SENTIMENT_SERVICE_H_
|