blob: dd0b93e7f05af7028da7206fa8ed3c1fd4cec75c [file] [log] [blame]
// Copyright 2023 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_HATS_SERVICE_DESKTOP_H_
#define CHROME_BROWSER_UI_HATS_HATS_SERVICE_DESKTOP_H_
#include <optional>
#include <set>
#include <string>
#include <string_view>
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/ui/hats/hats_service.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "content/public/browser/web_contents_observer.h"
class Browser;
class PrefService;
// Key-value mapping type for survey's product specific bits data.
typedef std::map<std::string, bool> SurveyBitsData;
// Key-value mapping type for survey's product specific string data.
typedef std::map<std::string, std::string> SurveyStringData;
// The name of the histogram which records if a survey was shown, or if not, the
// reason why not.
extern const char kHatsShouldShowSurveyReasonHistogram[];
// This class provides the client side logic for determining if a
// survey should be shown for any trigger based on input from a finch
// configuration. It is created on a per profile basis.
class HatsServiceDesktop : public HatsService {
public:
class DelayedSurveyTask : public content::WebContentsObserver {
public:
DelayedSurveyTask(HatsServiceDesktop* hats_service,
std::string trigger,
content::WebContents* web_contents,
const SurveyBitsData& product_specific_bits_data,
const SurveyStringData& product_specific_string_data,
NavigationBehavior navigation_behavior,
base::OnceClosure success_callback,
base::OnceClosure failure_callback,
std::optional<std::string_view> supplied_trigger_id);
// Not copyable or movable
DelayedSurveyTask(const DelayedSurveyTask&) = delete;
DelayedSurveyTask& operator=(const DelayedSurveyTask&) = delete;
~DelayedSurveyTask() override;
// Asks |hats_service_| to launch the survey with id |trigger_| for tab
// |web_contents_|.
void Launch();
// content::WebContentsObserver
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
void WebContentsDestroyed() override;
// Returns a weak pointer to this object.
virtual base::WeakPtr<DelayedSurveyTask> GetWeakPtr();
bool operator<(const HatsServiceDesktop::DelayedSurveyTask& other) const {
return trigger_ < other.trigger_ ? true
: web_contents() < other.web_contents();
}
private:
raw_ptr<HatsServiceDesktop> hats_service_;
std::string trigger_;
SurveyBitsData product_specific_bits_data_;
SurveyStringData product_specific_string_data_;
NavigationBehavior navigation_behavior_;
base::OnceClosure success_callback_;
base::OnceClosure failure_callback_;
std::optional<std::string> supplied_trigger_id_;
base::WeakPtrFactory<DelayedSurveyTask> weak_ptr_factory_{this};
};
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class ShouldShowSurveyReasons {
kYes = 0,
kNoOffline = 1,
kNoLastSessionCrashed = 2,
kNoReceivedSurveyInCurrentMilestone = 3,
kNoProfileTooNew = 4,
kNoLastSurveyTooRecent = 5,
kNoBelowProbabilityLimit = 6,
kNoTriggerStringMismatch = 7,
kNoWrongBrowserType = 8,
kNoIncognitoDisabled = 9,
kNoCookiesBlocked = 10, // Unused.
kNoThirdPartyCookiesBlocked = 11, // Unused.
kNoSurveyUnreachable = 12,
kNoSurveyOverCapacity = 13,
kNoSurveyAlreadyInProgress = 14,
kNoAnyLastSurveyTooRecent = 15,
kNoRejectedByHatsService = 16,
kNoLastSurveyCheckTooRecent = 17,
kMaxValue = kNoLastSurveyCheckTooRecent,
};
explicit HatsServiceDesktop(Profile* profile);
HatsServiceDesktop(const HatsServiceDesktop&) = delete;
HatsServiceDesktop& operator=(const HatsServiceDesktop&) = delete;
~HatsServiceDesktop() override;
static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
using HatsService::LaunchSurvey;
void LaunchSurvey(const std::string& trigger,
base::OnceClosure success_callback,
base::OnceClosure failure_callback,
const SurveyBitsData& product_specific_bits_data,
const SurveyStringData& product_specific_string_data,
const std::optional<std::string>& supplied_trigger_id,
const SurveyOptions& survey_options) override;
using HatsService::LaunchSurveyForWebContents;
void LaunchSurveyForWebContents(
const std::string& trigger,
content::WebContents* web_contents,
const SurveyBitsData& product_specific_bits_data,
const SurveyStringData& product_specific_string_data,
base::OnceClosure success_callback,
base::OnceClosure failure_callback,
const std::optional<std::string>& supplied_trigger_id,
const SurveyOptions& survey_options) override;
using HatsService::LaunchDelayedSurvey;
bool LaunchDelayedSurvey(
const std::string& trigger,
int timeout_ms,
const SurveyBitsData& product_specific_bits_data,
const SurveyStringData& product_specific_string_data) override;
using HatsService::LaunchDelayedSurveyForWebContents;
bool LaunchDelayedSurveyForWebContents(
const std::string& trigger,
content::WebContents* web_contents,
int timeout_ms,
const SurveyBitsData& product_specific_bits_data,
const SurveyStringData& product_specific_string_data,
NavigationBehavior navigation_behavior,
base::OnceClosure success_callback,
base::OnceClosure failure_callback,
const std::optional<std::string>& supplied_trigger_id,
const SurveyOptions& survey_options) override;
void SetSurveyMetadataForTesting(const HatsService::SurveyMetadata& metadata);
void GetSurveyMetadataForTesting(HatsService::SurveyMetadata* metadata) const;
bool HasPendingTasks();
bool CanShowSurvey(const std::string& trigger) const override;
bool CanShowAnySurvey(bool user_prompted) const override;
void RecordSurveyAsShown(std::string trigger_id) override;
// Indicates to the service that the HaTS Next dialog has been closed.
virtual void HatsNextDialogClosed();
// Returns whether a HaTS Next dialog currently exists, regardless of whether
// it is being shown or not.
bool hats_next_dialog_exists_for_testing() {
return hats_next_dialog_exists_;
}
protected:
private:
FRIEND_TEST_ALL_PREFIXES(HatsServiceProbabilityOne, SingleHatsNextDialog);
PrefService* GetPrefsForHatsMetadata() const;
// Remove |task| from the set of |pending_tasks_|.
void RemoveTask(const DelayedSurveyTask& task);
// Returns true is the survey trigger specified should be shown.
bool ShouldShowSurvey(const std::string& trigger) const;
// Returns true is the requested browser type matches the actual browser type.
bool IsRightBrowserType(
Browser* browser,
hats::SurveyConfig::RequestedBrowserType requested_browser_type) const;
void LaunchSurveyForBrowser(
Browser* browser,
const std::string& trigger,
base::OnceClosure success_callback,
base::OnceClosure failure_callback,
const SurveyBitsData& product_specific_bits_data,
const SurveyStringData& product_specific_string_data,
const std::optional<std::string_view>& supplied_trigger_id =
std::nullopt);
// Check whether the survey is reachable and under capacity and show it.
// |success_callback| is called when the survey is shown to the user.
// |failure_callback| is called if the survey does not launch for any reason.
// The matches of field names with the `SurveyConfig` are CHECK
// enforced.
void CheckSurveyStatusAndMaybeShow(
Browser* browser,
const std::string& trigger,
base::OnceClosure success_callback,
base::OnceClosure failure_callback,
const SurveyBitsData& product_specific_bits_data,
const SurveyStringData& product_specific_string_data,
const std::optional<std::string_view>& supplied_trigger_id);
std::set<DelayedSurveyTask> pending_tasks_;
// Whether a HaTS Next dialog currently exists (regardless of whether it
// is being shown to the user).
bool hats_next_dialog_exists_ = false;
base::WeakPtrFactory<HatsServiceDesktop> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_UI_HATS_HATS_SERVICE_DESKTOP_H_