#ifndef CHROME_BROWSER_COMPOSE_COMPOSE_SESSION_H_
#define CHROME_BROWSER_COMPOSE_COMPOSE_SESSION_H_
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include "base/check_op.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/timer/elapsed_timer.h"
#include "base/timer/timer.h"
#include "base/types/optional_ref.h"
#include "chrome/common/compose/compose.mojom.h"
#include "components/autofill/core/common/unique_ids.h"
#include "components/compose/core/browser/compose_metrics.h"
#include "components/content_extraction/content/browser/inner_text.h"
#include "components/optimization_guide/core/model_execution/multimodal_message.h"
#include "components/optimization_guide/core/model_execution/remote_model_executor.h"
#include "components/optimization_guide/core/model_quality/model_quality_logs_uploader_service.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
namespace base {
class ElapsedTimer;
}
namespace content {
class WebContents;
}
namespace content_extraction {
struct InnerTextResult;
}
namespace optimization_guide {
struct OptimizationGuideModelExecutionResult;
}
namespace ui {
struct AXTreeUpdate;
}
class InnerTextProvider {
public:
virtual void GetInnerText(content::RenderFrameHost& host,
std::optional<int> node_id,
content_extraction::InnerTextCallback callback) = 0;
protected:
virtual ~InnerTextProvider() = default;
};
class ComposeState;
class ComposeSession
: public compose::mojom::ComposeSessionUntrustedPageHandler {
public:
using ComposeCallback = base::OnceCallback<void(const std::u16string&)>;
class Observer {
public:
virtual void OnSessionComplete(
autofill::FieldGlobalId node_id,
compose::ComposeSessionCloseReason close_reason,
const compose::ComposeSessionEvents& events) = 0;
};
ComposeSession(content::WebContents* web_contents,
optimization_guide::RemoteModelExecutor* executor,
optimization_guide::ModelQualityLogsUploaderService*
model_quality_uploader,
base::Token session_id,
InnerTextProvider* inner_text,
autofill::FieldGlobalId node_id,
bool is_page_language_supported,
Observer* observer,
ComposeCallback callback = base::NullCallback());
~ComposeSession() override;
void Bind(mojo::PendingReceiver<
compose::mojom::ComposeSessionUntrustedPageHandler> handler,
mojo::PendingRemote<compose::mojom::ComposeUntrustedDialog> dialog);
void LogCancelEdit() override;
void Compose(const std::string& input,
compose::mojom::InputMode mode,
bool is_input_edited) override;
void Rewrite(compose::mojom::StyleModifier style) override;
void LogEditInput() override;
void RequestInitialState(RequestInitialStateCallback callback) override;
void SaveWebUIState(const std::string& webui_state) override;
void RecoverFromErrorState(RecoverFromErrorStateCallback callback) override;
void Undo(UndoCallback callback) override;
void Redo(RedoCallback callback) override;
void AcceptComposeResult(
AcceptComposeResultCallback success_callback) override;
void OpenBugReportingLink() override;
void OpenComposeLearnMorePage() override;
void OpenEnterpriseComposeLearnMorePage() override;
void OpenFeedbackSurveyLink() override;
void OpenSignInPage() override;
void SetUserFeedback(compose::mojom::UserFeedback feedback) override;
void EditResult(const std::string& new_result,
EditResultCallback callback) override;
void InitializeWithText(std::string_view selected_text);
void MaybeRefreshPageContext(bool has_selection);
bool CanShowFeedbackPage();
void OpenFeedbackPage(std::string feedback_id);
void SaveMostRecentOkStateToUndoStack();
void set_compose_callback(ComposeCallback callback) {
callback_ = std::move(callback);
}
void set_collect_inner_text(bool collect_inner_text) {
collect_inner_text_ = collect_inner_text;
}
bool get_current_msbb_state() { return current_msbb_state_; }
void set_current_msbb_state(bool current_msbb_state);
void set_fre_complete(bool fre_complete) { fre_complete_ = fre_complete; }
void set_msbb_settings_opened() {
session_events_.msbb_settings_opened = true;
}
bool get_fre_complete() { return fre_complete_; }
void set_started_with_proactive_nudge() {
session_events_.started_with_proactive_nudge = true;
}
void SetFirstRunCompleted();
void SetFirstRunCloseReason(
compose::ComposeFreOrMsbbSessionCloseReason close_reason);
void SetMSBBCloseReason(
compose::ComposeFreOrMsbbSessionCloseReason close_reason);
void SetCloseReason(compose::ComposeSessionCloseReason close_reason);
void LaunchHatsSurvey(compose::ComposeSessionCloseReason close_reason);
void SetSkipFeedbackUiForTesting(bool allowed);
bool HasExpired();
private:
void ProcessError(compose::EvalLocation eval_location,
compose::mojom::ComposeStatus status,
compose::ComposeRequestReason request_reason);
void ModelExecutionCallback(
const base::ElapsedTimer& request_start,
int request_id,
compose::ComposeRequestReason request_reason,
bool was_input_edited,
optimization_guide::OptimizationGuideModelExecutionResult result,
std::unique_ptr<optimization_guide::proto::ComposeLoggingData>
logging_data);
void ModelExecutionComplete(
base::TimeDelta request_delta,
compose::ComposeRequestReason request_reason,
bool was_input_edited,
optimization_guide::OptimizationGuideModelExecutionResult result,
std::unique_ptr<optimization_guide::ModelQualityLogEntry> log_entry);
void AddNewResponseToHistory(std::unique_ptr<ComposeState> new_state);
void EraseForwardStatesInHistory();
void MakeRequest(optimization_guide::proto::ComposeRequest request,
compose::ComposeRequestReason request_reason,
bool is_input_edited);
void RequestWithSession(
const optimization_guide::proto::ComposeRequest& request,
compose::ComposeRequestReason request_reason,
bool is_input_edited);
void ComposeRequestTimeout(int id);
void UpdateInnerTextAndContinueComposeIfNecessary(
int request_id,
std::unique_ptr<content_extraction::InnerTextResult> result);
void UpdateAXSnapshotAndContinueComposeIfNecessary(int request_id,
ui::AXTreeUpdate& update);
void TryContinueComposeWithContext();
bool HasNecessaryPageContext() const;
void SetQualityLogEntryUponError(
std::unique_ptr<optimization_guide::ModelQualityLogEntry>,
base::TimeDelta request_time,
bool was_input_edited);
void RefreshInnerText();
void RefreshAXSnapshot();
base::optional_ref<ComposeState> CurrentState(int offset = 0);
base::optional_ref<ComposeState> LastResponseState();
raw_ptr<optimization_guide::RemoteModelExecutor> executor_;
raw_ptr<optimization_guide::ModelQualityLogsUploaderService>
model_quality_uploader_;
mojo::Receiver<compose::mojom::ComposeSessionUntrustedPageHandler>
handler_receiver_;
mojo::Remote<compose::mojom::ComposeUntrustedDialog> dialog_remote_;
compose::mojom::ComposeStatePtr active_mojo_state_;
std::unique_ptr<optimization_guide::ModelQualityLogEntry>
most_recent_error_log_;
size_t history_current_index_ = 0;
std::vector<std::unique_ptr<ComposeState>> history_;
std::string initial_input_ = "";
bool currently_has_selection_ = false;
bool current_msbb_state_ = false;
bool msbb_initially_off_ = false;
compose::ComposeFreOrMsbbSessionCloseReason msbb_close_reason_{
compose::ComposeFreOrMsbbSessionCloseReason::kAbandoned};
bool fre_complete_ = false;
bool has_checked_autocompose_ = false;
compose::ComposeFreOrMsbbSessionCloseReason fre_close_reason_{
compose::ComposeFreOrMsbbSessionCloseReason::kAbandoned};
compose::ComposeSessionCloseReason close_reason_{
compose::ComposeSessionCloseReason::kAbandoned};
optimization_guide::proto::FinalStatus final_status_{
optimization_guide::proto::FinalStatus::STATUS_UNSPECIFIED};
optimization_guide::proto::FinalModelStatus final_model_status_{
optimization_guide::proto::FinalModelStatus::
FINAL_MODEL_STATUS_UNSPECIFIED};
std::unique_ptr<base::ElapsedTimer> session_duration_;
base::flat_map<int, std::unique_ptr<base::OneShotTimer>> request_timeouts_;
raw_ptr<content::WebContents> web_contents_;
raw_ptr<Observer> observer_;
ComposeCallback callback_;
optimization_guide::MultimodalMessage request_context_;
int request_id_ = 0;
int current_inner_text_request_id_ = 0;
int current_ax_snapshot_request_id_ = 0;
bool collect_inner_text_;
bool collect_ax_snapshot_ = false;
raw_ptr<InnerTextProvider> inner_text_caller_;
compose::ComposeSessionEvents session_events_;
ukm::SourceId ukm_source_id_;
bool got_inner_text_ = false;
bool got_ax_snapshot_ = false;
autofill::FieldGlobalId node_id_;
bool is_page_language_supported_;
base::OnceClosure continue_compose_;
base::Token session_id_;
bool skip_feedback_ui_for_testing_ = false;
std::optional<optimization_guide::proto::ComposePageMetadata> page_metadata_;
base::WeakPtrFactory<ComposeSession> weak_ptr_factory_;
};
#endif