blob: dfba33af2111ce8cb4175f3ed10ab1f262d200f1 [file] [log] [blame]
// Copyright 2025 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_ACTOR_EXECUTION_ENGINE_H_
#define CHROME_BROWSER_ACTOR_EXECUTION_ENGINE_H_
#include <memory>
#include <optional>
#include <vector>
#include "base/callback_list.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/safe_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/types/id_type.h"
#include "chrome/browser/actor/actor_task.h"
#include "chrome/browser/actor/aggregated_journal.h"
#include "chrome/browser/actor/tools/tool_controller.h"
#include "chrome/browser/actor/tools/tool_delegate.h"
#include "chrome/browser/password_manager/actor_login/actor_login_service.h"
#include "chrome/common/actor.mojom-forward.h"
#include "chrome/common/actor/task_id.h"
#include "components/tabs/public/tab_interface.h"
#include "content/public/browser/web_contents_observer.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_set.h"
class Profile;
namespace content {
class NavigationHandle;
}
namespace url {
class Origin;
} // namespace url
namespace actor {
class ActorTask;
class ToolRequest;
namespace ui {
class UiEventDispatcher;
}
// Coordinates the execution of a multi-step task.
class ExecutionEngine : public ToolDelegate {
public:
// State machine (success case)
//
// Init
// |
// v
// StartAction -> ToolCreateAndVerify ->
// ^ UiPreInvoke -> ToolInvoke -> UiPostInvoke -> Complete
// | | |
// |___________________________________________|______________|
//
// Complete may also be reached directly from other states in case of error.
enum class State {
kInit = 0,
kStartAction,
kToolCreateAndVerify,
kUiPreInvoke,
kToolInvoke,
kUiPostInvoke,
kComplete,
};
class StateObserver : public base::CheckedObserver {
public:
~StateObserver() override = default;
virtual void OnStateChanged(State old_state, State new_state) = 0;
};
explicit ExecutionEngine(Profile* profile);
ExecutionEngine(const ExecutionEngine&) = delete;
ExecutionEngine& operator=(const ExecutionEngine&) = delete;
~ExecutionEngine() override;
static std::unique_ptr<ExecutionEngine> CreateForTesting(
Profile* profile,
std::unique_ptr<ui::UiEventDispatcher> ui_event_dispatcher);
// This cannot be in the constructor as we first construct the
// ExecutionEngine, then the ActorTask.
void SetOwner(ActorTask* task);
// Cancels any in-progress actions with the reason: "kTaskPaused".
void CancelOngoingActions(mojom::ActionResultCode reason);
// If there is an ongoing tool request, treat it as having failed with the
// given reason.
void FailCurrentTool(mojom::ActionResultCode reason);
// Performs the given tool actions and invokes the callback when completed.
void Act(std::vector<std::unique_ptr<ToolRequest>>&& actions,
ActorTask::ActCallback callback);
// Invalidated anytime `action_sequence_` is reset.
base::WeakPtr<ExecutionEngine> GetWeakPtr();
// ToolDelegate:
Profile& GetProfile() override;
AggregatedJournal& GetJournal() override;
favicon::FaviconService* GetFaviconService() override;
actor_login::ActorLoginService& GetActorLoginService() override;
void PromptToSelectCredential(
const std::vector<actor_login::Credential>& credentials,
const base::flat_map<std::string, gfx::Image>& icons,
ToolDelegate::CredentialSelectedCallback callback) override;
void SetUserSelectedCredential(
const CredentialWithPermission& credential) override;
const std::optional<CredentialWithPermission> GetUserSelectedCredential(
const url::Origin& request_origin) const override;
void AddWritableMainframeOrigins(
const absl::flat_hash_set<url::Origin>& added_writable_mainframe_origins);
// Callback invoked when ConfirmCrossOriginNavigation, which spawns an IPC to
// the web client, receives its response. This callback gets a boolean
// indicating if navigation should continue.
using NavigationDecisionCallback =
base::OnceCallback<void(bool may_continue)>;
// Returns a boolean indicating if ActorNavigationThrottle should defer a
// navigation until the decision callback is invoked.
bool ShouldGateNavigation(content::NavigationHandle& navigation_handle,
NavigationDecisionCallback callback);
static std::string StateToString(State state);
void UserTakeover(mojom::ActionResultCode takeover_response_code,
base::OnceCallback<void(bool)> callback);
void RunUserTakeoverCallbackIfExists(bool should_cancel);
void set_user_take_over_result(
std::optional<mojom::ActionResultCode> user_takeover_result) {
user_takeover_result_ = user_takeover_result;
}
std::optional<mojom::ActionResultCode> user_take_over_result() const {
return user_takeover_result_;
}
void AddObserver(StateObserver* observer);
void RemoveObserver(StateObserver* observer);
private:
class NewTabWebContentsObserver;
// Used by tests only.
ExecutionEngine(Profile* profile,
std::unique_ptr<ui::UiEventDispatcher> ui_event_dispatcher);
void SetState(State state);
// Starts the next action by calling SafetyChecksForNextAction(). Must only be
// called if there is a next action.
void KickOffNextAction();
// Performs safety checks for next action. This is asynchronous.
void SafetyChecksForNextAction();
// Performs synchronous safety checks for the next action. If everything
// passes calls tool_controller_.Invoke().
void DidFinishAsyncSafetyChecks(const url::Origin& evaluated_origin,
bool may_act);
// If a failure occurs before the next action starts, we associate the tab
// that the action would have acted on with the task, so that we can provide
// tab observations back to the client.
void FailedOnTabBeforeToolCreation();
// Synchronously executes the next action. There are several types of actions,
// including renderer-scoped actions, tab-scoped actions, and global actions.
void ExecuteNextAction();
// Called each time an action finishes.
void PostToolCreate(mojom::ActionResultPtr result);
void FinishedUiPreInvoke(mojom::ActionResultPtr result);
void FinishedToolInvoke(mojom::ActionResultPtr result);
void FinishedUiPostInvoke(mojom::ActionResultPtr result);
void CompleteActions(mojom::ActionResultPtr result,
std::optional<size_t> action_index);
// Returns the next action that will be started when ExecuteNextAction is
// reached.
const ToolRequest& GetNextAction() const;
// Returns the index / action that was last executed and is still in progress.
// It is an error to call this when an action is not in progress.
size_t InProgressActionIndex() const;
const ToolRequest& GetInProgressAction() const;
// `std::nullopt` is returned when the decision to gate the navigation is done
// async.
std::optional<bool> ShouldGateNavigationInternal(
content::NavigationHandle& navigation_handle,
NavigationDecisionCallback callback);
void LogNavigationGating(const std::optional<url::Origin>& initiator_origin,
const GURL& navigation_url,
bool applied_gate);
void CheckNavigationBlocklist(
const std::optional<url::Origin>& initiator_origin,
const GURL& navigation_url,
bool skip_prompt,
NavigationDecisionCallback callback);
void OnNavigationBlocklistDecision(
const std::optional<url::Origin> initiator_origin,
const GURL navigation_url,
bool skip_prompt,
NavigationDecisionCallback callback,
bool not_on_blocklist);
// Called when the browser detects the actor needs to confirm a
// client-side-initiated navigation to a novel origin. The web client should
// check that the origin is relevant for the task and respond with whether the
// actor is permitted to visit the page.
void SendNavigationConfirmationRequest(const url::Origin& navigation_origin,
NavigationDecisionCallback callback);
void OnNavigationConfirmationDecision(
url::Origin navigation_origin,
NavigationDecisionCallback callback,
webui::mojom::NavigationConfirmationResponsePtr response);
// Called when the browser detects the actor navigating to an origin in the
// blocklist. The web client should confirm with the user that the actor is
// allowed to navigate to this origin.
void SendUserConfirmationDialogRequest(const url::Origin& navigation_origin,
NavigationDecisionCallback callback);
void OnPromptUserToConfirmNavigationDecision(
url::Origin navigation_origin,
NavigationDecisionCallback callback,
webui::mojom::UserConfirmationDialogResponsePtr response);
State state_ = State::kInit;
static std::optional<base::TimeDelta> action_observation_delay_for_testing_;
raw_ptr<Profile> profile_;
base::SafeRef<AggregatedJournal> journal_;
// Owns `this`.
raw_ptr<ActorTask> task_;
// Created when task_ is set. Handles execution details for an individual tool
// request.
std::unique_ptr<ToolController> tool_controller_;
std::unique_ptr<actor_login::ActorLoginService> actor_login_service_;
std::unique_ptr<ui::UiEventDispatcher> ui_event_dispatcher_;
std::vector<std::unique_ptr<ToolRequest>> action_sequence_;
ActorTask::ActCallback act_callback_;
// The index of the next action that will be started when ExecuteNextAction is
// reached.
size_t next_action_index_ = 0;
base::TimeTicks action_start_time_;
// If set, the currently executing tool should be considered failed once it
// completes.
std::optional<mojom::ActionResultCode> external_tool_failure_reason_;
// The results for actions so far.
std::vector<ActionResultWithLatencyInfo> action_results_;
// Origins which the browser is allowed to navigate to under actor control
// without prompting the user. This is applied to all navigations, including
// those initiated by the renderer with web content.
absl::flat_hash_set<url::Origin> allowed_navigation_origins_;
// For multi-step login, this is the credential that the user has chosen to
// allow the actor to use. The key is the
// `Credential::request_origin`.
base::flat_map<url::Origin, CredentialWithPermission>
user_selected_credentials_;
base::OnceCallback<void(bool /*should_cancel*/)> user_takeover_callback_;
std::optional<mojom::ActionResultCode> user_takeover_result_;
base::ObserverList<StateObserver> observers_;
SEQUENCE_CHECKER(sequence_checker_);
// Normally, a WeakPtrFactory only invalidates its WeakPtrs when the object is
// destroyed. However, this class invalidates WeakPtrs anytime a new set of
// actions is passed in. This effectively cancels any ongoing async actions.
base::WeakPtrFactory<ExecutionEngine> actions_weak_ptr_factory_{this};
};
std::ostream& operator<<(std::ostream& o, const ExecutionEngine::State& s);
} // namespace actor
#endif // CHROME_BROWSER_ACTOR_EXECUTION_ENGINE_H_