| // 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. |
| |
| #include "chrome/browser/ui/hats/trust_safety_sentiment_service.h" |
| |
| #include "base/containers/cxx20_erase.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/rand_util.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| #include "chrome/browser/ui/hats/hats_service.h" |
| #include "chrome/browser/ui/hats/hats_service_factory.h" |
| #include "chrome/browser/ui/webui/settings/site_settings_helper.h" |
| #include "chrome/common/channel_info.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/webui_url_constants.h" |
| #include "components/content_settings/core/browser/cookie_settings.h" |
| #include "components/content_settings/core/browser/host_content_settings_map.h" |
| #include "components/content_settings/core/browser/website_settings_registry.h" |
| #include "components/content_settings/core/common/pref_names.h" |
| #include "components/password_manager/core/common/password_manager_pref_names.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/privacy_sandbox/privacy_sandbox_prefs.h" |
| #include "components/safe_browsing/core/common/safe_browsing_prefs.h" |
| #include "components/signin/public/base/signin_pref_names.h" |
| #include "components/unified_consent/pref_names.h" |
| #include "components/version_info/channel.h" |
| |
| namespace { |
| |
| base::TimeDelta GetMinTimeToPrompt() { |
| return base::FeatureList::IsEnabled(features::kTrustSafetySentimentSurveyV2) |
| ? features::kTrustSafetySentimentSurveyV2MinTimeToPrompt.Get() |
| : features::kTrustSafetySentimentSurveyMinTimeToPrompt.Get(); |
| } |
| |
| base::TimeDelta GetMaxTimeToPrompt() { |
| return base::FeatureList::IsEnabled(features::kTrustSafetySentimentSurveyV2) |
| ? features::kTrustSafetySentimentSurveyV2MaxTimeToPrompt.Get() |
| : features::kTrustSafetySentimentSurveyMaxTimeToPrompt.Get(); |
| } |
| |
| base::TimeDelta GetMinSessionTime() { |
| DCHECK(base::FeatureList::IsEnabled(features::kTrustSafetySentimentSurveyV2)); |
| return features::kTrustSafetySentimentSurveyV2MinSessionTime.Get(); |
| } |
| |
| int GetRequiredNtpCount() { |
| return base::FeatureList::IsEnabled(features::kTrustSafetySentimentSurveyV2) |
| ? base::RandInt( |
| features::kTrustSafetySentimentSurveyV2NtpVisitsMinRange |
| .Get(), |
| features::kTrustSafetySentimentSurveyV2NtpVisitsMaxRange |
| .Get()) |
| : base::RandInt( |
| features::kTrustSafetySentimentSurveyNtpVisitsMinRange.Get(), |
| features::kTrustSafetySentimentSurveyNtpVisitsMaxRange |
| .Get()); |
| } |
| |
| int GetMaxRequiredNtpCount() { |
| return base::FeatureList::IsEnabled(features::kTrustSafetySentimentSurveyV2) |
| ? features::kTrustSafetySentimentSurveyV2NtpVisitsMaxRange.Get() |
| : features::kTrustSafetySentimentSurveyNtpVisitsMaxRange.Get(); |
| } |
| |
| bool HasNonDefaultPrivacySetting(Profile* profile) { |
| auto* prefs = profile->GetPrefs(); |
| |
| std::vector<std::string> prefs_to_check = { |
| prefs::kSafeBrowsingEnabled, |
| prefs::kSafeBrowsingEnhanced, |
| prefs::kSafeBrowsingScoutReportingEnabled, |
| prefs::kEnableDoNotTrack, |
| password_manager::prefs::kPasswordLeakDetectionEnabled, |
| prefs::kCookieControlsMode, |
| }; |
| |
| bool has_non_default_pref = false; |
| for (const auto& pref_name : prefs_to_check) { |
| auto* pref = prefs->FindPreference(pref_name); |
| if (!pref->IsDefaultValue() && pref->IsUserControlled()) { |
| has_non_default_pref = true; |
| break; |
| } |
| } |
| |
| // Users consenting to sync automatically enable UKM collection |
| auto* ukm_pref = prefs->FindPreference( |
| unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled); |
| auto* sync_consent_pref = |
| prefs->FindPreference(prefs::kGoogleServicesConsentedToSync); |
| |
| bool has_non_default_ukm = |
| ukm_pref->GetValue()->GetBool() != |
| sync_consent_pref->GetValue()->GetBool() && |
| (ukm_pref->IsUserControlled() || sync_consent_pref->IsUserControlled()); |
| |
| // Check the default value for each user facing content setting. Note that |
| // this will not include content setting exceptions set via permission |
| // prompts, as they are site specific. |
| bool has_non_default_content_setting = false; |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile); |
| |
| for (auto content_setting_type : |
| site_settings::GetVisiblePermissionCategories()) { |
| std::string content_setting_provider; |
| auto current_value = map->GetDefaultContentSetting( |
| content_setting_type, &content_setting_provider); |
| auto content_setting_source = |
| HostContentSettingsMap::GetSettingSourceFromProviderName( |
| content_setting_provider); |
| |
| const bool user_controlled = |
| content_setting_source == |
| content_settings::SettingSource::SETTING_SOURCE_NONE || |
| content_setting_source == |
| content_settings::SettingSource::SETTING_SOURCE_USER; |
| |
| auto default_value = static_cast<ContentSetting>( |
| content_settings::WebsiteSettingsRegistry::GetInstance() |
| ->Get(content_setting_type) |
| ->initial_default_value() |
| .GetInt()); |
| |
| if (current_value != default_value && user_controlled) { |
| has_non_default_content_setting = true; |
| break; |
| } |
| } |
| |
| return has_non_default_pref || has_non_default_ukm || |
| has_non_default_content_setting; |
| } |
| |
| // Generates the Product Specific Data which accompanies survey results for the |
| // Privacy Settings product area. This includes whether the user is receiving |
| // the survey because they ran safety check, and whether they have any |
| // non-default core privacy settings. |
| std::map<std::string, bool> GetPrivacySettingsProductSpecificData( |
| Profile* profile, |
| bool ran_safety_check) { |
| std::map<std::string, bool> product_specific_data; |
| product_specific_data["Non default setting"] = |
| HasNonDefaultPrivacySetting(profile); |
| product_specific_data["Ran safety check"] = ran_safety_check; |
| return product_specific_data; |
| } |
| |
| } // namespace |
| |
| TrustSafetySentimentService::TrustSafetySentimentService(Profile* profile) |
| : profile_(profile) { |
| DCHECK(profile); |
| observed_profiles_.AddObservation(profile); |
| |
| // As this service is created lazily, there may already be a primary OTR |
| // profile created for the main profile. |
| if (auto* primary_otr = |
| profile->GetPrimaryOTRProfile(/*create_if_needed=*/false)) { |
| observed_profiles_.AddObservation(primary_otr); |
| } |
| |
| if (base::FeatureList::IsEnabled(features::kTrustSafetySentimentSurveyV2)) { |
| metrics::DesktopSessionDurationTracker::Get()->AddObserver(this); |
| performed_control_group_dice_roll_ = false; |
| } |
| } |
| |
| TrustSafetySentimentService::~TrustSafetySentimentService() { |
| if (base::FeatureList::IsEnabled(features::kTrustSafetySentimentSurveyV2)) { |
| metrics::DesktopSessionDurationTracker::Get()->RemoveObserver(this); |
| } |
| } |
| |
| void TrustSafetySentimentService::OpenedNewTabPage() { |
| // Explicit early exit for the common path, where the user has not performed |
| // any of the trigger actions. |
| if (pending_triggers_.size() == 0) |
| return; |
| |
| // Reduce the NTPs to open count for all the active triggers. |
| for (auto& area_trigger : pending_triggers_) { |
| auto& trigger = area_trigger.second; |
| if (trigger.remaining_ntps_to_open > 0) |
| trigger.remaining_ntps_to_open--; |
| } |
| |
| // Cleanup any triggers which are no longer relevant. This will be every |
| // trigger which occurred more than the maximum prompt time ago, or the |
| // trigger for the kIneligible area if it is no longer blocking |
| // eligibility. |
| base::EraseIf(pending_triggers_, |
| [](const std::pair<FeatureArea, PendingTrigger>& area_trigger) { |
| return base::Time::Now() - area_trigger.second.occurred_time > |
| GetMaxTimeToPrompt() || |
| (area_trigger.first == FeatureArea::kIneligible && |
| !ShouldBlockSurvey(area_trigger.second)); |
| }); |
| |
| // This may have emptied the set of pending triggers. |
| if (pending_triggers_.size() == 0) |
| return; |
| |
| // A primary OTR profile (incognito) existing will prevent any surveys from |
| // being shown. |
| if (profile_->HasPrimaryOTRProfile()) |
| return; |
| |
| // Check if any of the triggers make the user not yet eligible to receive a |
| // survey. |
| for (const auto& area_trigger : pending_triggers_) { |
| if (ShouldBlockSurvey(area_trigger.second)) |
| return; |
| } |
| |
| // Choose a trigger at random to avoid any order biasing. |
| auto winning_area_iterator = pending_triggers_.begin(); |
| std::advance(winning_area_iterator, |
| base::RandInt(0, pending_triggers_.size() - 1)); |
| |
| // The winning feature area should never be kIneligible, as this will |
| // have either been removed above, or blocked showing any survey. |
| DCHECK(winning_area_iterator->first != FeatureArea::kIneligible); |
| |
| HatsService* hats_service = |
| HatsServiceFactory::GetForProfile(profile_, /*create_if_necessary=*/true); |
| |
| // A null HaTS service should have prevented this service from being created. |
| DCHECK(hats_service); |
| hats_service->LaunchSurvey( |
| GetHatsTriggerForFeatureArea(winning_area_iterator->first), |
| /*success_callback=*/base::DoNothing(), |
| /*failure_callback=*/base::DoNothing(), |
| winning_area_iterator->second.product_specific_data); |
| base::UmaHistogramEnumeration("Feedback.TrustSafetySentiment.SurveyRequested", |
| winning_area_iterator->first); |
| pending_triggers_.clear(); |
| } |
| |
| void TrustSafetySentimentService::InteractedWithPrivacySettings( |
| content::WebContents* web_contents) { |
| // Only observe one instance settings at a time. This ignores both multiple |
| // instances of settings, and repeated interactions with settings. This |
| // reduces the chance that a user is eligible for a survey, but is much |
| // simpler. As interactions with settings (visiting password manager and using |
| // the privacy card) can occur independently, there is also little risk of |
| // starving one interaction. |
| if (settings_watcher_) |
| return; |
| |
| settings_watcher_ = std::make_unique<SettingsWatcher>( |
| web_contents, |
| features::kTrustSafetySentimentSurveyPrivacySettingsTime.Get(), |
| base::BindOnce(&TrustSafetySentimentService::TriggerOccurred, |
| weak_ptr_factory_.GetWeakPtr(), |
| FeatureArea::kPrivacySettings, |
| GetPrivacySettingsProductSpecificData( |
| profile_, /*ran_safety_check=*/false)), |
| base::BindOnce(&TrustSafetySentimentService::SettingsWatcherComplete, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void TrustSafetySentimentService::RanSafetyCheck() { |
| // Since we have logic to block a trigger for an incorrect version, we can |
| // call both of these and only the appropriate trigger and probability will be |
| // recorded. |
| TriggerOccurred(FeatureArea::kSafetyCheck, {}); |
| TriggerOccurred(FeatureArea::kPrivacySettings, |
| GetPrivacySettingsProductSpecificData( |
| profile_, /*ran_safety_check=*/true)); |
| } |
| |
| void TrustSafetySentimentService::PageInfoOpened() { |
| // Only one Page Info should ever be open. |
| DCHECK(!page_info_state_); |
| page_info_state_ = std::make_unique<PageInfoState>(); |
| } |
| |
| void TrustSafetySentimentService::InteractedWithPageInfo() { |
| DCHECK(page_info_state_); |
| page_info_state_->interacted = true; |
| } |
| |
| void TrustSafetySentimentService::PageInfoClosed() { |
| DCHECK(page_info_state_); |
| |
| base::TimeDelta threshold = |
| base::FeatureList::IsEnabled(features::kTrustSafetySentimentSurveyV2) |
| ? features::kTrustSafetySentimentSurveyV2TrustedSurfaceTime.Get() |
| : features::kTrustSafetySentimentSurveyTrustedSurfaceTime.Get(); |
| // Record a trigger if either the user had page info open for the required |
| // time, or if they interacted with it. |
| if (base::Time::Now() - page_info_state_->opened_time >= threshold || |
| page_info_state_->interacted) { |
| TriggerOccurred( |
| FeatureArea::kTrustedSurface, |
| {{"Interacted with Page Info", page_info_state_->interacted}}); |
| } |
| |
| page_info_state_.reset(); |
| } |
| |
| void TrustSafetySentimentService::SavedPassword() { |
| TriggerOccurred(FeatureArea::kTransactions, {{"Saved password", true}}); |
| } |
| |
| void TrustSafetySentimentService::OpenedPasswordManager( |
| content::WebContents* web_contents) { |
| if (settings_watcher_) |
| return; |
| |
| std::map<std::string, bool> product_specific_data = { |
| {"Saved password", false}}; |
| |
| settings_watcher_ = std::make_unique<SettingsWatcher>( |
| web_contents, |
| features::kTrustSafetySentimentSurveyTransactionsPasswordManagerTime |
| .Get(), |
| base::BindOnce(&TrustSafetySentimentService::TriggerOccurred, |
| weak_ptr_factory_.GetWeakPtr(), FeatureArea::kTransactions, |
| product_specific_data), |
| base::BindOnce(&TrustSafetySentimentService::SettingsWatcherComplete, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void TrustSafetySentimentService::SavedCard() { |
| TriggerOccurred(FeatureArea::kTransactions, {{"Saved password", false}}); |
| } |
| |
| void TrustSafetySentimentService::RanPasswordCheck() { |
| TriggerOccurred(FeatureArea::kPasswordCheck, {}); |
| } |
| |
| void TrustSafetySentimentService::ClearedBrowsingData( |
| browsing_data::BrowsingDataType datatype) { |
| // We are only interested in history, downloads, and autofill. |
| switch (datatype) { |
| case (browsing_data::BrowsingDataType::HISTORY): |
| case (browsing_data::BrowsingDataType::DOWNLOADS): |
| case (browsing_data::BrowsingDataType::FORM_DATA): |
| break; |
| default: |
| return; |
| } |
| return TriggerOccurred( |
| FeatureArea::kBrowsingData, |
| {{"Deleted history", |
| datatype == browsing_data::BrowsingDataType::HISTORY}, |
| {"Deleted downloads", |
| datatype == browsing_data::BrowsingDataType::DOWNLOADS}, |
| {"Deleted autofill form data", |
| datatype == browsing_data::BrowsingDataType::FORM_DATA}}); |
| ; |
| } |
| |
| void TrustSafetySentimentService::FinishedPrivacyGuide() { |
| TriggerOccurred(FeatureArea::kPrivacyGuide, {}); |
| } |
| |
| void TrustSafetySentimentService::InteractedWithPrivacySandbox3( |
| FeatureArea feature_area) { |
| std::map<std::string, bool> product_specific_data; |
| product_specific_data["Stable channel"] = |
| (chrome::GetChannel() == version_info::Channel::STABLE) ? true : false; |
| bool blockCookies = |
| HostContentSettingsMapFactory::GetForProfile(profile_) |
| ->GetDefaultContentSetting(ContentSettingsType::COOKIES, |
| /*provider_id=*/nullptr) == |
| ContentSetting::CONTENT_SETTING_BLOCK; |
| blockCookies = |
| blockCookies || |
| (static_cast<content_settings::CookieControlsMode>( |
| profile_->GetPrefs()->GetInteger(prefs::kCookieControlsMode)) == |
| content_settings::CookieControlsMode::kBlockThirdParty); |
| product_specific_data["3P cookies blocked"] = blockCookies ? true : false; |
| product_specific_data["Privacy Sandbox enabled"] = |
| profile_->GetPrefs()->GetBoolean(prefs::kPrivacySandboxApisEnabledV2) |
| ? true |
| : false; |
| TriggerOccurred(feature_area, product_specific_data); |
| } |
| |
| void TrustSafetySentimentService::InteractedWithPrivacySandbox4( |
| FeatureArea feature_area) { |
| TriggerOccurred(feature_area, {}); |
| } |
| |
| void TrustSafetySentimentService::OnOffTheRecordProfileCreated( |
| Profile* off_the_record) { |
| // Only interested in the primary OTR profile i.e. the one used for incognito |
| // browsing. Non-primary OTR profiles are often used as implementation details |
| // of other features, and are not inherintly relevant to Trust & Safety. |
| if (off_the_record->GetOTRProfileID() == Profile::OTRProfileID::PrimaryID()) |
| observed_profiles_.AddObservation(off_the_record); |
| } |
| |
| void TrustSafetySentimentService::OnProfileWillBeDestroyed(Profile* profile) { |
| observed_profiles_.RemoveObservation(profile); |
| |
| if (profile->IsOffTheRecord()) { |
| // Closing the incognito profile, which is the only OTR profie observed by |
| // this class, is an ileligible action. |
| PerformedIneligibleAction(); |
| } |
| } |
| |
| void TrustSafetySentimentService::OnSessionEnded(base::TimeDelta session_length, |
| base::TimeTicks session_end) { |
| DCHECK(base::FeatureList::IsEnabled(features::kTrustSafetySentimentSurveyV2)); |
| // Check if the user is eligible for the control group. |
| if (!performed_control_group_dice_roll_ && |
| session_length > GetMinSessionTime()) { |
| performed_control_group_dice_roll_ = true; |
| TriggerOccurred(FeatureArea::kControlGroup, {}); |
| } |
| } |
| |
| TrustSafetySentimentService::PendingTrigger::PendingTrigger( |
| const std::map<std::string, bool>& product_specific_data, |
| int remaining_ntps_to_open) |
| : product_specific_data(product_specific_data), |
| remaining_ntps_to_open(remaining_ntps_to_open), |
| occurred_time(base::Time::Now()) {} |
| |
| TrustSafetySentimentService::PendingTrigger::PendingTrigger( |
| int remaining_ntps_to_open) |
| : remaining_ntps_to_open(remaining_ntps_to_open), |
| occurred_time(base::Time::Now()) {} |
| |
| TrustSafetySentimentService::PendingTrigger::PendingTrigger() = default; |
| TrustSafetySentimentService::PendingTrigger::~PendingTrigger() = default; |
| TrustSafetySentimentService::PendingTrigger::PendingTrigger( |
| const PendingTrigger& other) = default; |
| |
| TrustSafetySentimentService::SettingsWatcher::SettingsWatcher( |
| content::WebContents* web_contents, |
| base::TimeDelta required_open_time, |
| base::OnceCallback<void()> success_callback, |
| base::OnceCallback<void()> complete_callback) |
| : web_contents_(web_contents), |
| success_callback_(std::move(success_callback)), |
| complete_callback_(std::move(complete_callback)) { |
| base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce( |
| &TrustSafetySentimentService::SettingsWatcher::TimerComplete, |
| weak_ptr_factory_.GetWeakPtr()), |
| required_open_time); |
| Observe(web_contents); |
| } |
| |
| TrustSafetySentimentService::SettingsWatcher::~SettingsWatcher() = default; |
| |
| void TrustSafetySentimentService::SettingsWatcher::WebContentsDestroyed() { |
| std::move(complete_callback_).Run(); |
| } |
| |
| void TrustSafetySentimentService::SettingsWatcher::TimerComplete() { |
| const bool stayed_on_settings = |
| web_contents_ && |
| web_contents_->GetVisibility() == content::Visibility::VISIBLE && |
| web_contents_->GetLastCommittedURL().host_piece() == |
| chrome::kChromeUISettingsHost; |
| if (stayed_on_settings) |
| std::move(success_callback_).Run(); |
| |
| std::move(complete_callback_).Run(); |
| } |
| |
| TrustSafetySentimentService::PageInfoState::PageInfoState() |
| : opened_time(base::Time::Now()) {} |
| |
| void TrustSafetySentimentService::SettingsWatcherComplete() { |
| settings_watcher_.reset(); |
| } |
| |
| void TrustSafetySentimentService::TriggerOccurred( |
| FeatureArea feature_area, |
| const std::map<std::string, bool>& product_specific_data) { |
| if (!ProbabilityCheck(feature_area)) |
| return; |
| |
| base::UmaHistogramEnumeration("Feedback.TrustSafetySentiment.TriggerOccurred", |
| feature_area); |
| |
| // This will overwrite any previous trigger for this feature area. We are |
| // only interested in the most recent trigger, so this is acceptable. |
| pending_triggers_[feature_area] = |
| PendingTrigger(product_specific_data, GetRequiredNtpCount()); |
| } |
| |
| void TrustSafetySentimentService::PerformedIneligibleAction() { |
| pending_triggers_[FeatureArea::kIneligible] = |
| PendingTrigger(GetMaxRequiredNtpCount()); |
| |
| base::UmaHistogramEnumeration("Feedback.TrustSafetySentiment.TriggerOccurred", |
| FeatureArea::kIneligible); |
| } |
| |
| /*static*/ bool TrustSafetySentimentService::ShouldBlockSurvey( |
| const PendingTrigger& trigger) { |
| return base::Time::Now() - trigger.occurred_time < GetMinTimeToPrompt() || |
| trigger.remaining_ntps_to_open > 0; |
| } |
| |
| // static |
| bool TrustSafetySentimentService::VersionCheck(FeatureArea feature_area) { |
| bool isV2 = |
| base::FeatureList::IsEnabled(features::kTrustSafetySentimentSurveyV2); |
| switch (feature_area) { |
| // Version 1 only |
| case (FeatureArea::kPrivacySettings): |
| case (FeatureArea::kTransactions): |
| case (FeatureArea::kPrivacySandbox3ConsentAccept): |
| case (FeatureArea::kPrivacySandbox3ConsentDecline): |
| case (FeatureArea::kPrivacySandbox3NoticeDismiss): |
| case (FeatureArea::kPrivacySandbox3NoticeOk): |
| case (FeatureArea::kPrivacySandbox3NoticeSettings): |
| case (FeatureArea::kPrivacySandbox3NoticeLearnMore): |
| return isV2 == false; |
| // Version 2 only |
| case (FeatureArea::kSafetyCheck): |
| case (FeatureArea::kPasswordCheck): |
| case (FeatureArea::kBrowsingData): |
| case (FeatureArea::kPrivacyGuide): |
| case (FeatureArea::kControlGroup): |
| return isV2 == true; |
| // Both Versions |
| case (FeatureArea::kTrustedSurface): |
| case (FeatureArea::kPrivacySandbox4ConsentAccept): |
| case (FeatureArea::kPrivacySandbox4ConsentDecline): |
| case (FeatureArea::kPrivacySandbox4NoticeOk): |
| case (FeatureArea::kPrivacySandbox4NoticeSettings): |
| return true; |
| // None |
| case (FeatureArea::kIneligible): |
| return false; |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| } |
| |
| // static |
| std::string TrustSafetySentimentService::GetHatsTriggerForFeatureArea( |
| FeatureArea feature_area) { |
| if (base::FeatureList::IsEnabled(features::kTrustSafetySentimentSurveyV2)) { |
| switch (feature_area) { |
| case (FeatureArea::kTrustedSurface): |
| return kHatsSurveyTriggerTrustSafetyV2TrustedSurface; |
| case (FeatureArea::kSafetyCheck): |
| return kHatsSurveyTriggerTrustSafetyV2SafetyCheck; |
| case (FeatureArea::kPasswordCheck): |
| return kHatsSurveyTriggerTrustSafetyV2PasswordCheck; |
| case (FeatureArea::kBrowsingData): |
| return kHatsSurveyTriggerTrustSafetyV2BrowsingData; |
| case (FeatureArea::kPrivacyGuide): |
| return kHatsSurveyTriggerTrustSafetyV2PrivacyGuide; |
| case (FeatureArea::kControlGroup): |
| return kHatsSurveyTriggerTrustSafetyV2ControlGroup; |
| case (FeatureArea::kPrivacySandbox4ConsentAccept): |
| return kHatsSurveyTriggerTrustSafetyV2PrivacySandbox4ConsentAccept; |
| case (FeatureArea::kPrivacySandbox4ConsentDecline): |
| return kHatsSurveyTriggerTrustSafetyV2PrivacySandbox4ConsentDecline; |
| case (FeatureArea::kPrivacySandbox4NoticeOk): |
| return kHatsSurveyTriggerTrustSafetyV2PrivacySandbox4NoticeOk; |
| case (FeatureArea::kPrivacySandbox4NoticeSettings): |
| return kHatsSurveyTriggerTrustSafetyV2PrivacySandbox4NoticeSettings; |
| default: |
| NOTREACHED(); |
| return ""; |
| } |
| } |
| switch (feature_area) { |
| case (FeatureArea::kPrivacySettings): |
| return kHatsSurveyTriggerTrustSafetyPrivacySettings; |
| case (FeatureArea::kTrustedSurface): |
| return kHatsSurveyTriggerTrustSafetyTrustedSurface; |
| case (FeatureArea::kTransactions): |
| return kHatsSurveyTriggerTrustSafetyTransactions; |
| case (FeatureArea::kPrivacySandbox3ConsentAccept): |
| return kHatsSurveyTriggerTrustSafetyPrivacySandbox3ConsentAccept; |
| case (FeatureArea::kPrivacySandbox3ConsentDecline): |
| return kHatsSurveyTriggerTrustSafetyPrivacySandbox3ConsentDecline; |
| case (FeatureArea::kPrivacySandbox3NoticeDismiss): |
| return kHatsSurveyTriggerTrustSafetyPrivacySandbox3NoticeDismiss; |
| case (FeatureArea::kPrivacySandbox3NoticeOk): |
| return kHatsSurveyTriggerTrustSafetyPrivacySandbox3NoticeOk; |
| case (FeatureArea::kPrivacySandbox3NoticeSettings): |
| return kHatsSurveyTriggerTrustSafetyPrivacySandbox3NoticeSettings; |
| case (FeatureArea::kPrivacySandbox3NoticeLearnMore): |
| return kHatsSurveyTriggerTrustSafetyPrivacySandbox3NoticeLearnMore; |
| case (FeatureArea::kPrivacySandbox4ConsentAccept): |
| return kHatsSurveyTriggerTrustSafetyPrivacySandbox4ConsentAccept; |
| case (FeatureArea::kPrivacySandbox4ConsentDecline): |
| return kHatsSurveyTriggerTrustSafetyPrivacySandbox4ConsentDecline; |
| case (FeatureArea::kPrivacySandbox4NoticeOk): |
| return kHatsSurveyTriggerTrustSafetyPrivacySandbox4NoticeOk; |
| case (FeatureArea::kPrivacySandbox4NoticeSettings): |
| return kHatsSurveyTriggerTrustSafetyPrivacySandbox4NoticeSettings; |
| default: |
| NOTREACHED(); |
| return ""; |
| } |
| } |
| |
| // static |
| bool TrustSafetySentimentService::ProbabilityCheck(FeatureArea feature_area) { |
| if (!TrustSafetySentimentService::VersionCheck(feature_area)) { |
| return false; |
| } |
| |
| if (base::FeatureList::IsEnabled(features::kTrustSafetySentimentSurveyV2)) { |
| switch (feature_area) { |
| case (FeatureArea::kTrustedSurface): |
| return base::RandDouble() < |
| features::kTrustSafetySentimentSurveyV2TrustedSurfaceProbability |
| .Get(); |
| case (FeatureArea::kSafetyCheck): |
| return base::RandDouble() < |
| features::kTrustSafetySentimentSurveyV2SafetyCheckProbability |
| .Get(); |
| case (FeatureArea::kPasswordCheck): |
| return base::RandDouble() < |
| features::kTrustSafetySentimentSurveyV2PasswordCheckProbability |
| .Get(); |
| case (FeatureArea::kBrowsingData): |
| return base::RandDouble() < |
| features::kTrustSafetySentimentSurveyV2BrowsingDataProbability |
| .Get(); |
| case (FeatureArea::kPrivacyGuide): |
| return base::RandDouble() < |
| features::kTrustSafetySentimentSurveyV2PrivacyGuideProbability |
| .Get(); |
| case (FeatureArea::kControlGroup): |
| return base::RandDouble() < |
| features::kTrustSafetySentimentSurveyV2ControlGroupProbability |
| .Get(); |
| case (FeatureArea::kPrivacySandbox4ConsentAccept): |
| return base::RandDouble() < |
| features:: |
| kTrustSafetySentimentSurveyV2PrivacySandbox4ConsentAcceptProbability |
| .Get(); |
| case (FeatureArea::kPrivacySandbox4ConsentDecline): |
| return base::RandDouble() < |
| features:: |
| kTrustSafetySentimentSurveyV2PrivacySandbox4ConsentDeclineProbability |
| .Get(); |
| case (FeatureArea::kPrivacySandbox4NoticeOk): |
| return base::RandDouble() < |
| features:: |
| kTrustSafetySentimentSurveyV2PrivacySandbox4NoticeOkProbability |
| .Get(); |
| case (FeatureArea::kPrivacySandbox4NoticeSettings): |
| return base::RandDouble() < |
| features:: |
| kTrustSafetySentimentSurveyV2PrivacySandbox4NoticeSettingsProbability |
| .Get(); |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| } |
| |
| switch (feature_area) { |
| case (FeatureArea::kPrivacySettings): |
| return base::RandDouble() < |
| features::kTrustSafetySentimentSurveyPrivacySettingsProbability |
| .Get(); |
| case (FeatureArea::kTrustedSurface): |
| return base::RandDouble() < |
| features::kTrustSafetySentimentSurveyTrustedSurfaceProbability |
| .Get(); |
| case (FeatureArea::kTransactions): |
| return base::RandDouble() < |
| features::kTrustSafetySentimentSurveyTransactionsProbability.Get(); |
| case (FeatureArea::kPrivacySandbox3ConsentAccept): |
| return base::RandDouble() < |
| features:: |
| kTrustSafetySentimentSurveyPrivacySandbox3ConsentAcceptProbability |
| .Get(); |
| case (FeatureArea::kPrivacySandbox3ConsentDecline): |
| return base::RandDouble() < |
| features:: |
| kTrustSafetySentimentSurveyPrivacySandbox3ConsentDeclineProbability |
| .Get(); |
| case (FeatureArea::kPrivacySandbox3NoticeDismiss): |
| return base::RandDouble() < |
| features:: |
| kTrustSafetySentimentSurveyPrivacySandbox3NoticeDismissProbability |
| .Get(); |
| case (FeatureArea::kPrivacySandbox3NoticeOk): |
| return base::RandDouble() < |
| features:: |
| kTrustSafetySentimentSurveyPrivacySandbox3NoticeOkProbability |
| .Get(); |
| case (FeatureArea::kPrivacySandbox3NoticeSettings): |
| return base::RandDouble() < |
| features:: |
| kTrustSafetySentimentSurveyPrivacySandbox3NoticeSettingsProbability |
| .Get(); |
| case (FeatureArea::kPrivacySandbox3NoticeLearnMore): |
| return base::RandDouble() < |
| features:: |
| kTrustSafetySentimentSurveyPrivacySandbox3NoticeLearnMoreProbability |
| .Get(); |
| case (FeatureArea::kPrivacySandbox4ConsentAccept): |
| return base::RandDouble() < |
| features:: |
| kTrustSafetySentimentSurveyPrivacySandbox4ConsentAcceptProbability |
| .Get(); |
| case (FeatureArea::kPrivacySandbox4ConsentDecline): |
| return base::RandDouble() < |
| features:: |
| kTrustSafetySentimentSurveyPrivacySandbox4ConsentDeclineProbability |
| .Get(); |
| case (FeatureArea::kPrivacySandbox4NoticeOk): |
| return base::RandDouble() < |
| features:: |
| kTrustSafetySentimentSurveyPrivacySandbox4NoticeOkProbability |
| .Get(); |
| case (FeatureArea::kPrivacySandbox4NoticeSettings): |
| return base::RandDouble() < |
| features:: |
| kTrustSafetySentimentSurveyPrivacySandbox4NoticeSettingsProbability |
| .Get(); |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| } |