123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- // Copyright 2021 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 "components/accuracy_tips/accuracy_service.h"
- #include <utility>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/callback_forward.h"
- #include "base/containers/contains.h"
- #include "base/memory/scoped_refptr.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/observer_list.h"
- #include "base/time/time.h"
- #include "base/values.h"
- #include "components/accuracy_tips/accuracy_tip_interaction.h"
- #include "components/accuracy_tips/accuracy_tip_safe_browsing_client.h"
- #include "components/accuracy_tips/accuracy_tip_status.h"
- #include "components/accuracy_tips/features.h"
- #include "components/accuracy_tips/pref_names.h"
- #include "components/pref_registry/pref_registry_syncable.h"
- #include "components/prefs/pref_service.h"
- #include "components/prefs/scoped_user_pref_update.h"
- #include "components/unified_consent/pref_names.h"
- #include "content/public/browser/web_contents.h"
- #include "services/metrics/public/cpp/metrics_utils.h"
- #include "services/metrics/public/cpp/ukm_builders.h"
- #include "url/gurl.h"
- namespace accuracy_tips {
- // Returns a suffix for accuracy tips histograms.
- // Needs to match AccuracyTipInteractions from histogram_suffixes_list.xml.
- const std::string GetHistogramSuffix(AccuracyTipInteraction interaction) {
- switch (interaction) {
- case AccuracyTipInteraction::kNoAction:
- return "NoAction";
- case AccuracyTipInteraction::kLearnMore:
- return "LearnMore";
- case AccuracyTipInteraction::kOptOut:
- return "OptOut";
- case AccuracyTipInteraction::kClosed:
- return "Closed";
- case AccuracyTipInteraction::kIgnore:
- return "Ignore";
- case AccuracyTipInteraction::kPermissionRequested:
- return "PermissionRequested";
- case AccuracyTipInteraction::kDisabledByExperiment:
- NOTREACHED(); // We don't need specific histograms for this.
- return "";
- }
- }
- // Returns the preference to store feature state in. Uses a different pref if
- // the UI is disabled to avoid dark-launch experiments affecting real usage.
- const char* GetLastShownPrefName(bool disable_ui) {
- return disable_ui ? prefs::kLastAccuracyTipShownDisabledUi
- : prefs::kLastAccuracyTipShown;
- }
- const char* GetPreviousInteractionsPrefName(bool disable_ui) {
- return disable_ui ? prefs::kPreviousInteractionsDisabledUi
- : prefs::kPreviousInteractions;
- }
- using AccuracyCheckCallback = AccuracyService::AccuracyCheckCallback;
- // static
- void AccuracyService::RegisterProfilePrefs(
- user_prefs::PrefRegistrySyncable* registry) {
- const bool disable_ui = features::kDisableUi.Get();
- registry->RegisterTimePref(GetLastShownPrefName(disable_ui), base::Time());
- registry->RegisterListPref(GetPreviousInteractionsPrefName(disable_ui));
- }
- AccuracyService::AccuracyService(
- std::unique_ptr<Delegate> delegate,
- PrefService* pref_service,
- scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> sb_database,
- history::HistoryService* history_service,
- scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
- scoped_refptr<base::SequencedTaskRunner> io_task_runner)
- : delegate_(std::move(delegate)),
- pref_service_(pref_service),
- ui_task_runner_(ui_task_runner),
- sample_url_(GURL(features::kSampleUrl.Get())),
- time_between_prompts_(features::kTimeBetweenPrompts.Get()),
- disable_ui_(features::kDisableUi.Get()) {
- if (sb_database) {
- sb_client_ = base::MakeRefCounted<AccuracyTipSafeBrowsingClient>(
- std::move(sb_database), std::move(ui_task_runner),
- std::move(io_task_runner));
- }
- if (history_service) {
- history_service_observation_.Observe(history_service);
- }
- }
- AccuracyService::~AccuracyService() = default;
- void AccuracyService::Shutdown() {
- if (sb_client_) {
- sb_client_->Shutdown();
- sb_client_ = nullptr;
- }
- history_service_observation_.Reset();
- }
- void AccuracyService::CheckAccuracyStatus(const GURL& url,
- AccuracyCheckCallback callback) {
- DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
- const base::Value::List& last_interactions =
- pref_service_->GetValueList(GetPreviousInteractionsPrefName(disable_ui_));
- const base::Value opt_out_value(
- static_cast<int>(AccuracyTipInteraction::kOptOut));
- if (base::Contains(last_interactions, opt_out_value)) {
- return std::move(callback).Run(AccuracyTipStatus::kOptOut);
- }
- base::Time last_shown =
- pref_service_->GetTime(GetLastShownPrefName(disable_ui_));
- if (clock_->Now() - last_shown < time_between_prompts_) {
- return std::move(callback).Run(AccuracyTipStatus::kRateLimited);
- }
- if (sample_url_.is_valid() && url == sample_url_) {
- return OnAccuracyStatusReceived(url, std::move(callback),
- AccuracyTipStatus::kShowAccuracyTip);
- }
- if (!sb_client_) {
- return std::move(callback).Run(AccuracyTipStatus::kNone);
- }
- sb_client_->CheckAccuracyStatus(
- url,
- base::BindOnce(&AccuracyService::OnAccuracyStatusReceived,
- weak_factory_.GetWeakPtr(), url, std::move(callback)));
- }
- void AccuracyService::OnAccuracyStatusReceived(const GURL& url,
- AccuracyCheckCallback callback,
- AccuracyTipStatus status) {
- if (status == AccuracyTipStatus::kShowAccuracyTip &&
- delegate_->IsEngagementHigh(url)) {
- return std::move(callback).Run(AccuracyTipStatus::kHighEnagagement);
- }
- return std::move(callback).Run(status);
- }
- void AccuracyService::MaybeShowAccuracyTip(content::WebContents* web_contents) {
- DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
- pref_service_->SetTime(GetLastShownPrefName(disable_ui_), clock_->Now());
- if (disable_ui_) {
- return OnAccuracyTipClosed(
- base::TimeTicks(),
- web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId(),
- AccuracyTipInteraction::kDisabledByExperiment);
- }
- bool show_opt_out =
- pref_service_->GetValueList(GetPreviousInteractionsPrefName(disable_ui_))
- .size() >= static_cast<size_t>(features::kNumIgnorePrompts.Get());
- url_for_last_shown_tip_ = web_contents->GetLastCommittedURL();
- web_contents_showing_accuracy_tip_ = web_contents;
- delegate_->ShowAccuracyTip(
- web_contents, AccuracyTipStatus::kShowAccuracyTip,
- /*show_opt_out=*/show_opt_out,
- base::BindOnce(
- &AccuracyService::OnAccuracyTipClosed, weak_factory_.GetWeakPtr(),
- base::TimeTicks::Now(),
- web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId()));
- for (Observer& observer : observers_)
- observer.OnAccuracyTipShown();
- }
- void AccuracyService::MaybeShowSurvey() {
- if (CanShowSurvey()) {
- const auto& interactions_list = pref_service_->GetValueList(
- GetPreviousInteractionsPrefName(disable_ui_));
- const int last_interaction = interactions_list.back().GetInt();
- const bool ukm_enabled = pref_service_->GetBoolean(
- unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled);
- std::string url_parameter_for_hats =
- ukm_enabled ? url_for_last_shown_tip_.DeprecatedGetOriginAsURL().spec()
- : "";
- delegate_->ShowSurvey(
- {}, {{"Tip shown for URL", url_parameter_for_hats},
- {"UI interaction", base::NumberToString(last_interaction)}});
- }
- }
- bool AccuracyService::IsSecureConnection(content::WebContents* web_contents) {
- return delegate_->IsSecureConnection(web_contents);
- }
- void AccuracyService::OnURLsDeleted(
- history::HistoryService* history_service,
- const history::DeletionInfo& deletion_info) {
- if (deletion_info.time_range().IsValid()) {
- base::Time last_shown =
- pref_service_->GetTime(GetLastShownPrefName(disable_ui_));
- if (last_shown >= deletion_info.time_range().begin() &&
- last_shown <= deletion_info.time_range().end()) {
- url_for_last_shown_tip_ = GURL();
- }
- } else {
- if (deletion_info.deleted_urls_origin_map().count(
- url_for_last_shown_tip_.DeprecatedGetOriginAsURL())) {
- url_for_last_shown_tip_ = GURL();
- }
- }
- }
- void AccuracyService::AddObserver(Observer* observer) {
- observers_.AddObserver(observer);
- }
- void AccuracyService::RemoveObserver(Observer* observer) {
- observers_.RemoveObserver(observer);
- }
- bool AccuracyService::IsShowingAccuracyTip(content::WebContents* web_contents) {
- return web_contents_showing_accuracy_tip_ != nullptr &&
- web_contents_showing_accuracy_tip_ == web_contents;
- }
- void AccuracyService::OnAccuracyTipClosed(base::TimeTicks time_opened,
- ukm::SourceId ukm_source_id,
- AccuracyTipInteraction interaction) {
- DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
- ListPrefUpdate update(pref_service_,
- GetPreviousInteractionsPrefName(disable_ui_));
- base::Value* interaction_list = update.Get();
- interaction_list->Append(static_cast<int>(interaction));
- // Record metrics.
- base::UmaHistogramEnumeration("Privacy.AccuracyTip.AccuracyTipInteraction",
- interaction);
- base::UmaHistogramCounts100("Privacy.AccuracyTip.NumDialogsShown",
- interaction_list->GetListDeprecated().size());
- ukm::builders::AccuracyTipDialog ukm_builder(ukm_source_id);
- ukm_builder.SetInteraction(static_cast<int>(interaction));
- if (interaction != AccuracyTipInteraction::kDisabledByExperiment) {
- const base::TimeDelta time_open = base::TimeTicks::Now() - time_opened;
- base::UmaHistogramMediumTimes("Privacy.AccuracyTip.AccuracyTipTimeOpen",
- time_open);
- ukm_builder.SetTimeSpent(
- ukm::GetExponentialBucketMinForFineUserTiming(time_open.InSeconds()));
- const std::string suffix = GetHistogramSuffix(interaction);
- base::UmaHistogramCounts100("Privacy.AccuracyTip.NumDialogsShown." + suffix,
- interaction_list->GetListDeprecated().size());
- base::UmaHistogramMediumTimes(
- "Privacy.AccuracyTip.AccuracyTipTimeOpen." + suffix, time_open);
- }
- ukm_builder.Record(ukm::UkmRecorder::Get());
- web_contents_showing_accuracy_tip_ = nullptr;
- for (Observer& observer : observers_)
- observer.OnAccuracyTipClosed();
- }
- bool AccuracyService::CanShowSurvey() {
- if (!base::FeatureList::IsEnabled(
- accuracy_tips::features::kAccuracyTipsSurveyFeature) ||
- disable_ui_) {
- return false;
- }
- base::Time last_shown =
- pref_service_->GetTime(GetLastShownPrefName(disable_ui_));
- base::TimeDelta last_shown_delta = clock_->Now() - last_shown;
- const bool has_required_time_passed =
- last_shown_delta >= features::kMinTimeToShowSurvey.Get() &&
- last_shown_delta <= features::kMaxTimeToShowSurvey.Get();
- if (!has_required_time_passed)
- return false;
- if (url_for_last_shown_tip_.is_empty() || !url_for_last_shown_tip_.is_valid())
- return false;
- int interactions_count =
- pref_service_->GetValueList(GetPreviousInteractionsPrefName(disable_ui_))
- .size();
- return interactions_count >= features::kMinPromptCountRequiredForSurvey.Get();
- }
- } // namespace accuracy_tips
|