#ifndef CHROME_BROWSER_COMPOSE_PROACTIVE_NUDGE_TRACKER_H_
#define CHROME_BROWSER_COMPOSE_PROACTIVE_NUDGE_TRACKER_H_
#include <map>
#include <memory>
#include <optional>
#include <string>
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "chrome/browser/compose/proto/compose_optimization_guide.pb.h"
#include "components/autofill/core/browser/foundations/scoped_autofill_managers_observation.h"
#include "components/autofill/core/browser/suggestions/suggestion.h"
#include "components/autofill/core/common/unique_ids.h"
#include "components/compose/core/browser/compose_metrics.h"
#include "components/segmentation_platform/public/result.h"
namespace content {
class WebContents;
}
namespace segmentation_platform {
class SegmentationPlatformService;
}
namespace compose {
class ProactiveNudgeTracker : public autofill::AutofillManager::Observer {
public:
using FallbackShowResult = base::RepeatingCallback<float()>;
class Delegate {
public:
virtual void ShowProactiveNudge(autofill::FormGlobalId form,
autofill::FieldGlobalId field,
compose::ComposeEntryPoint entry_point) = 0;
virtual compose::PageUkmTracker* GetPageUkmTracker() = 0;
virtual compose::ComposeHintMetadata GetComposeHintMetadata() = 0;
virtual float SegmentationFallbackShowResult();
virtual float SegmentationForceShowResult();
};
enum class ShowState {
kInitial,
kWaitingForTimerToStop,
kTimerCanceled,
kWaitingForSegmentation,
kWaitingForProactiveNudgeRequest,
kBlockedBySegmentation,
kShown
};
struct Signals {
Signals();
Signals(Signals&&);
Signals& operator=(Signals&&);
~Signals();
ukm::SourceId ukm_source_id;
url::Origin page_origin;
GURL page_url;
autofill::FormData form;
autofill::FormFieldData field;
base::TimeTicks page_change_time;
};
class State final {
public:
State();
~State();
Signals signals;
std::u16string initial_text_value;
std::optional<segmentation_platform::ClassificationResult>
segmentation_result;
bool segmentation_result_ignored_for_training = false;
base::OneShotTimer timer;
bool selection_nudge_requested = false;
bool selection_nudge_shown = false;
bool timer_canceled = false;
int text_change_count = 0;
ShowState show_state = ShowState::kInitial;
base::WeakPtr<State> AsWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); }
private:
base::WeakPtrFactory<State> weak_ptr_factory_{this};
};
ProactiveNudgeTracker(
segmentation_platform::SegmentationPlatformService* segmentation_service,
Delegate* delegate);
~ProactiveNudgeTracker() override;
void StartObserving(content::WebContents* web_contents);
bool ProactiveNudgeRequestedForFormField(Signals signals);
bool IsTimerRunning();
void FocusChangedInPage();
void Clear();
void ComposeSessionCompleted(autofill::FieldGlobalId field_renderer_id,
ComposeSessionCloseReason session_close_reason,
const compose::ComposeSessionEvents& events);
void OnUserDisabledNudge(bool single_site_only);
void OnAfterFocusOnFormField(autofill::AutofillManager& manager,
autofill::FormGlobalId form,
autofill::FieldGlobalId field) override;
void OnAfterCaretMovedInFormField(autofill::AutofillManager& manager,
const autofill::FormGlobalId& form,
const autofill::FieldGlobalId& field,
const std::u16string& selection,
const gfx::Rect& caret_bounds) override;
void OnAfterTextFieldValueChanged(autofill::AutofillManager& manager,
autofill::FormGlobalId form,
autofill::FieldGlobalId field,
const std::u16string& text_value) override;
private:
class EngagementTracker;
void ResetState();
void UpdateStateForCurrentFormField();
std::optional<ShowState> CheckForStateTransition();
void TransitionToState(ShowState new_show_state);
void BeginWaitingForTimerToStop();
void BeginTimerCanceled();
void BeginSegmentation();
void BeginWaitingForProactiveNudgeRequest();
void BeginBlockedBySegmentation();
void BeginShown();
void ShowTimerElapsed();
void StartOrRestartTimer();
bool CanStartFocusTimer();
bool CanStartTextSettledTimer();
bool CanStartSelectionTimer();
void GotClassificationResult(
const segmentation_platform::ClassificationResult& result);
bool MatchesCurrentField(autofill::FormGlobalId form,
autofill::FieldGlobalId field);
void CollectTrainingData(
const segmentation_platform::TrainingRequestId training_request_id,
ProactiveNudgeDerivedEngagement engagement);
std::optional<bool> CachedSegmentationResult();
std::unique_ptr<State> state_;
bool nudge_currently_requested_ = false;
std::map<autofill::FieldGlobalId, bool> seen_fields_;
std::map<autofill::FieldGlobalId, std::unique_ptr<EngagementTracker>>
engagement_trackers_;
const raw_ptr<segmentation_platform::SegmentationPlatformService>
segmentation_service_;
const raw_ptr<Delegate> delegate_;
autofill::ScopedAutofillManagersObservation autofill_managers_observation_{
this};
base::WeakPtrFactory<ProactiveNudgeTracker> weak_ptr_factory_{this};
};
}
#endif