blob: c74f1a8378c4b7a9dcdacca7dcb1fbe2ed51efbc [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.
#include "chrome/browser/actor/browser_action_util.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <optional>
#include "base/barrier_closure.h"
#include "base/base64.h"
#include "base/functional/callback_helpers.h"
#include "base/notimplemented.h"
#include "base/numerics/safe_conversions.h"
#include "base/types/expected.h"
#include "chrome/browser/actor/actor_keyed_service.h"
#include "chrome/browser/actor/actor_task.h"
#include "chrome/browser/actor/shared_types.h"
#include "chrome/browser/actor/tools/attempt_login_tool_request.h"
#include "chrome/browser/actor/tools/click_tool_request.h"
#include "chrome/browser/actor/tools/drag_and_release_tool_request.h"
#include "chrome/browser/actor/tools/history_tool_request.h"
#include "chrome/browser/actor/tools/move_mouse_tool_request.h"
#include "chrome/browser/actor/tools/navigate_tool_request.h"
#include "chrome/browser/actor/tools/script_tool_request.h"
#include "chrome/browser/actor/tools/scroll_tool_request.h"
#include "chrome/browser/actor/tools/select_tool_request.h"
#include "chrome/browser/actor/tools/tab_management_tool_request.h"
#include "chrome/browser/actor/tools/tool_request.h"
#include "chrome/browser/actor/tools/type_tool_request.h"
#include "chrome/browser/actor/tools/wait_tool_request.h"
#include "chrome/browser/page_content_annotations/multi_source_page_context_fetcher.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/common/actor/action_result.h"
#include "chrome/common/actor/actor_constants.h"
#include "chrome/common/actor/actor_logging.h"
#include "components/optimization_guide/content/browser/page_content_proto_provider.h"
#include "components/optimization_guide/proto/features/actions_data.pb.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/window_open_disposition.h"
namespace actor {
// Alias the namespace to make the long enums a bit more readable in
// implementations.
namespace apc = ::optimization_guide::proto;
using apc::Action;
using apc::ActionTarget;
using apc::ActivateTabAction;
using apc::AttemptLoginAction;
using apc::ClickAction;
using apc::CloseTabAction;
using apc::CreateTabAction;
using apc::DragAndReleaseAction;
using apc::HistoryBackAction;
using apc::HistoryForwardAction;
using apc::MoveMouseAction;
using apc::NavigateAction;
using apc::ScriptToolAction;
using apc::ScrollAction;
using apc::SelectAction;
using apc::TypeAction;
using apc::WaitAction;
using ::optimization_guide::DocumentIdentifierUserData;
using ::page_content_annotations::FetchPageContextOptions;
using ::page_content_annotations::FetchPageContextResult;
using ::page_content_annotations::FetchPageContextResultCallbackArg;
using ::tabs::TabHandle;
using ::tabs::TabInterface;
namespace {
struct PageScopedParams {
std::string document_identifier;
TabHandle tab_handle;
};
template <class T>
TabHandle GetTabHandle(const T& action, TabInterface* deprecated_fallback_tab) {
tabs::TabHandle tab_handle;
if (action.has_tab_id()) {
tab_handle = TabHandle(action.tab_id());
} else if (deprecated_fallback_tab) {
tab_handle = deprecated_fallback_tab->GetHandle();
}
return tab_handle;
}
std::optional<PageTarget> ToPageTarget(
const optimization_guide::proto::ActionTarget& target) {
// A valid target must have either a coordinate or a
// document_identifier-dom_node_id pair.
if (target.has_coordinate()) {
return PageTarget(
gfx::Point(target.coordinate().x(), target.coordinate().y()));
} else {
if (!target.has_content_node_id() || !target.has_document_identifier()) {
return std::nullopt;
}
return PageTarget(
DomNode{.node_id = target.content_node_id(),
.document_identifier =
target.document_identifier().serialized_token()});
}
}
std::unique_ptr<ToolRequest> CreateClickRequest(
const ClickAction& action,
TabInterface* deprecated_fallback_tab) {
TabHandle tab_handle = GetTabHandle(action, deprecated_fallback_tab);
if (!action.has_target() || !action.has_click_count() ||
!action.has_click_type() || tab_handle == TabHandle::Null()) {
return nullptr;
}
MouseClickCount count;
switch (action.click_count()) {
case apc::ClickAction_ClickCount_SINGLE:
count = MouseClickCount::kSingle;
break;
case apc::ClickAction_ClickCount_DOUBLE:
count = MouseClickCount::kDouble;
break;
case apc::ClickAction_ClickCount_UNKNOWN_CLICK_COUNT:
case apc::
ClickAction_ClickCount_ClickAction_ClickCount_INT_MIN_SENTINEL_DO_NOT_USE_:
case apc::
ClickAction_ClickCount_ClickAction_ClickCount_INT_MAX_SENTINEL_DO_NOT_USE_:
// TODO(crbug.com/412700289): Revert once this is set.
count = MouseClickCount::kSingle;
break;
}
MouseClickType type;
switch (action.click_type()) {
case apc::ClickAction_ClickType_LEFT:
type = MouseClickType::kLeft;
break;
case apc::ClickAction_ClickType_RIGHT:
type = MouseClickType::kRight;
break;
case apc::
ClickAction_ClickType_ClickAction_ClickType_INT_MIN_SENTINEL_DO_NOT_USE_:
case apc::
ClickAction_ClickType_ClickAction_ClickType_INT_MAX_SENTINEL_DO_NOT_USE_:
case apc::ClickAction_ClickType_UNKNOWN_CLICK_TYPE:
// TODO(crbug.com/412700289): Revert once this is set.
type = MouseClickType::kLeft;
break;
}
auto target = ToPageTarget(action.target());
if (!target.has_value()) {
return nullptr;
}
return std::make_unique<ClickToolRequest>(tab_handle, target.value(), type,
count);
}
std::unique_ptr<ToolRequest> CreateTypeRequest(
const TypeAction& action,
TabInterface* deprecated_fallback_tab) {
using TypeMode = TypeToolRequest::Mode;
TabHandle tab_handle = GetTabHandle(action, deprecated_fallback_tab);
if (!action.has_target() || !action.has_text() || !action.has_mode() ||
!action.has_follow_by_enter() || tab_handle == TabHandle::Null()) {
return nullptr;
}
TypeMode mode;
switch (action.mode()) {
case apc::TypeAction_TypeMode_DELETE_EXISTING:
mode = TypeMode::kReplace;
break;
case apc::TypeAction_TypeMode_PREPEND:
mode = TypeMode::kPrepend;
break;
case apc::TypeAction_TypeMode_APPEND:
mode = TypeMode::kAppend;
break;
case apc::TypeAction_TypeMode_UNKNOWN_TYPE_MODE:
case apc::
TypeAction_TypeMode_TypeAction_TypeMode_INT_MIN_SENTINEL_DO_NOT_USE_:
case apc::
TypeAction_TypeMode_TypeAction_TypeMode_INT_MAX_SENTINEL_DO_NOT_USE_:
// TODO(crbug.com/412700289): Revert once this is set.
mode = TypeMode::kReplace;
break;
}
auto target = ToPageTarget(action.target());
if (!target.has_value()) {
return nullptr;
}
return std::make_unique<TypeToolRequest>(tab_handle, target.value(),
action.text(),
action.follow_by_enter(), mode);
}
std::unique_ptr<ToolRequest> CreateScrollRequest(
const ScrollAction& action,
TabInterface* deprecated_fallback_tab) {
using Direction = ScrollToolRequest::Direction;
TabHandle tab_handle = GetTabHandle(action, deprecated_fallback_tab);
if (!action.has_direction() || !action.has_distance() ||
tab_handle == TabHandle::Null()) {
return nullptr;
}
std::optional<PageTarget> target;
if (action.has_target()) {
target = ToPageTarget(action.target());
} else {
// Scroll action may omit a target which means "target the viewport".
TabInterface* tab = tab_handle.Get();
if (!tab) {
return nullptr;
}
std::string document_identifier =
DocumentIdentifierUserData::GetOrCreateForCurrentDocument(
tab->GetContents()->GetPrimaryMainFrame())
->serialized_token();
target.emplace(
PageTarget(DomNode{.node_id = kRootElementDomNodeId,
.document_identifier = document_identifier}));
}
if (!target) {
return nullptr;
}
Direction direction;
switch (action.direction()) {
case apc::ScrollAction_ScrollDirection_LEFT:
direction = Direction::kLeft;
break;
case apc::ScrollAction_ScrollDirection_RIGHT:
direction = Direction::kRight;
break;
case apc::ScrollAction_ScrollDirection_UP:
direction = Direction::kUp;
break;
case apc::ScrollAction_ScrollDirection_DOWN:
direction = Direction::kDown;
break;
case apc::ScrollAction_ScrollDirection_UNKNOWN_SCROLL_DIRECTION:
case apc::
ScrollAction_ScrollDirection_ScrollAction_ScrollDirection_INT_MIN_SENTINEL_DO_NOT_USE_:
case apc::
ScrollAction_ScrollDirection_ScrollAction_ScrollDirection_INT_MAX_SENTINEL_DO_NOT_USE_:
// TODO(crbug.com/412700289): Revert once this is set.
direction = Direction::kDown;
break;
}
return std::make_unique<ScrollToolRequest>(tab_handle, target.value(),
direction, action.distance());
}
std::unique_ptr<ToolRequest> CreateMoveMouseRequest(
const MoveMouseAction& action,
TabInterface* deprecated_fallback_tab) {
TabHandle tab_handle = GetTabHandle(action, deprecated_fallback_tab);
if (!action.has_target() || tab_handle == TabHandle::Null()) {
return nullptr;
}
auto target = ToPageTarget(action.target());
if (!target.has_value()) {
return nullptr;
}
return std::make_unique<MoveMouseToolRequest>(tab_handle, target.value());
}
std::unique_ptr<ToolRequest> CreateDragAndReleaseRequest(
const DragAndReleaseAction& action,
TabInterface* deprecated_fallback_tab) {
TabHandle tab_handle = GetTabHandle(action, deprecated_fallback_tab);
if (!action.has_from_target() || !action.has_to_target() ||
tab_handle == TabHandle::Null()) {
return nullptr;
}
auto from_target = ToPageTarget(action.from_target());
if (!from_target.has_value()) {
return nullptr;
}
auto to_target = ToPageTarget(action.to_target());
if (!to_target.has_value()) {
return nullptr;
}
return std::make_unique<DragAndReleaseToolRequest>(
tab_handle, from_target.value(), to_target.value());
}
std::unique_ptr<ToolRequest> CreateSelectRequest(
const SelectAction& action,
TabInterface* deprecated_fallback_tab) {
TabHandle tab_handle = GetTabHandle(action, deprecated_fallback_tab);
if (!action.has_value() || !action.has_target() ||
tab_handle == TabHandle::Null()) {
return nullptr;
}
auto target = ToPageTarget(action.target());
if (!target.has_value()) {
return nullptr;
}
return std::make_unique<SelectToolRequest>(tab_handle, target.value(),
action.value());
}
std::unique_ptr<ToolRequest> CreateNavigateRequest(
const NavigateAction& action,
TabInterface* deprecated_fallback_tab) {
TabHandle tab_handle = GetTabHandle(action, deprecated_fallback_tab);
if (!action.has_url() || tab_handle == TabHandle::Null()) {
return nullptr;
}
return std::make_unique<NavigateToolRequest>(tab_handle, GURL(action.url()));
}
std::unique_ptr<ToolRequest> CreateCreateTabRequest(
const CreateTabAction& action) {
if (!action.has_window_id()) {
return nullptr;
}
int32_t window_id = action.window_id();
// TODO(bokan): Is the foreground bit always set? If not, should this return
// an error or default to what? For now we default to foreground.
WindowOpenDisposition disposition =
!action.has_foreground() || action.foreground()
? WindowOpenDisposition::NEW_FOREGROUND_TAB
: WindowOpenDisposition::NEW_BACKGROUND_TAB;
return std::make_unique<CreateTabToolRequest>(window_id, disposition);
}
std::unique_ptr<ToolRequest> CreateActivateTabRequest(
const ActivateTabAction& action,
TabInterface* deprecated_fallback_tab) {
tabs::TabHandle tab_handle;
if (action.has_tab_id()) {
tab_handle = tabs::TabHandle(action.tab_id());
} else if (deprecated_fallback_tab) {
tab_handle = deprecated_fallback_tab->GetHandle();
} else {
return nullptr;
}
return std::make_unique<ActivateTabToolRequest>(tab_handle);
}
std::unique_ptr<ToolRequest> CreateCloseTabRequest(
const CloseTabAction& action,
TabInterface* deprecated_fallback_tab) {
tabs::TabHandle tab_handle;
if (action.has_tab_id()) {
tab_handle = tabs::TabHandle(action.tab_id());
} else if (deprecated_fallback_tab) {
tab_handle = deprecated_fallback_tab->GetHandle();
} else {
return nullptr;
}
return std::make_unique<CloseTabToolRequest>(tab_handle);
}
std::unique_ptr<ToolRequest> CreateBackRequest(
const HistoryBackAction& action,
TabInterface* deprecated_fallback_tab) {
tabs::TabHandle tab_handle;
if (action.has_tab_id()) {
tab_handle = tabs::TabHandle(action.tab_id());
} else if (deprecated_fallback_tab) {
tab_handle = deprecated_fallback_tab->GetHandle();
} else {
return nullptr;
}
return std::make_unique<HistoryToolRequest>(
tab_handle, HistoryToolRequest::Direction::kBack);
}
std::unique_ptr<ToolRequest> CreateForwardRequest(
const HistoryForwardAction& action,
TabInterface* deprecated_fallback_tab) {
tabs::TabHandle tab_handle;
if (action.has_tab_id()) {
tab_handle = tabs::TabHandle(action.tab_id());
} else if (deprecated_fallback_tab) {
tab_handle = deprecated_fallback_tab->GetHandle();
} else {
return nullptr;
}
return std::make_unique<HistoryToolRequest>(
tab_handle, HistoryToolRequest::Direction::kForward);
}
std::unique_ptr<ToolRequest> CreateWaitRequest(const WaitAction& action) {
constexpr base::TimeDelta kWaitTime = base::Seconds(3);
return std::make_unique<WaitToolRequest>(kWaitTime);
}
std::unique_ptr<ToolRequest> CreateAttemptLoginRequest(
const AttemptLoginAction& action,
TabInterface* deprecated_fallback_tab) {
const tabs::TabHandle tab_handle =
GetTabHandle(action, deprecated_fallback_tab);
if (tab_handle == TabHandle::Null()) {
return nullptr;
}
return std::make_unique<AttemptLoginToolRequest>(tab_handle);
}
std::unique_ptr<ToolRequest> CreateScriptToolRequest(
const ScriptToolAction& action,
TabInterface* deprecated_fallback_tab) {
const tabs::TabHandle tab_handle =
GetTabHandle(action, deprecated_fallback_tab);
if (tab_handle == TabHandle::Null()) {
return nullptr;
}
return std::make_unique<ScriptToolRequest>(
tab_handle,
PageTarget(DomNode{.node_id = kRootElementDomNodeId,
.document_identifier =
action.document_identifier().serialized_token()}),
action.tool_name(), action.input_arguments());
}
} // namespace
std::unique_ptr<ToolRequest> CreateToolRequest(
const optimization_guide::proto::Action& action,
TabInterface* deprecated_fallback_tab) {
switch (action.action_case()) {
case optimization_guide::proto::Action::kClick: {
const ClickAction& click_action = action.click();
return CreateClickRequest(click_action, deprecated_fallback_tab);
}
case optimization_guide::proto::Action::kType: {
const TypeAction& type_action = action.type();
return CreateTypeRequest(type_action, deprecated_fallback_tab);
}
case optimization_guide::proto::Action::kScroll: {
const ScrollAction& scroll_action = action.scroll();
return CreateScrollRequest(scroll_action, deprecated_fallback_tab);
}
case optimization_guide::proto::Action::kMoveMouse: {
const MoveMouseAction& move_mouse_action = action.move_mouse();
return CreateMoveMouseRequest(move_mouse_action, deprecated_fallback_tab);
}
case optimization_guide::proto::Action::kDragAndRelease: {
const DragAndReleaseAction& drag_action = action.drag_and_release();
return CreateDragAndReleaseRequest(drag_action, deprecated_fallback_tab);
}
case optimization_guide::proto::Action::kSelect: {
const SelectAction& select_action = action.select();
return CreateSelectRequest(select_action, deprecated_fallback_tab);
}
case optimization_guide::proto::Action::kNavigate: {
const NavigateAction& navigate_action = action.navigate();
return CreateNavigateRequest(navigate_action, deprecated_fallback_tab);
}
case optimization_guide::proto::Action::kBack: {
const HistoryBackAction& back_action = action.back();
return CreateBackRequest(back_action, deprecated_fallback_tab);
}
case optimization_guide::proto::Action::kForward: {
const HistoryForwardAction& forward_action = action.forward();
return CreateForwardRequest(forward_action, deprecated_fallback_tab);
}
case optimization_guide::proto::Action::kWait: {
const WaitAction& wait_action = action.wait();
return CreateWaitRequest(wait_action);
}
case optimization_guide::proto::Action::kCreateTab: {
const CreateTabAction& create_tab_action = action.create_tab();
return CreateCreateTabRequest(create_tab_action);
}
case optimization_guide::proto::Action::kCloseTab: {
const CloseTabAction& close_tab_action = action.close_tab();
return CreateCloseTabRequest(close_tab_action, deprecated_fallback_tab);
}
case optimization_guide::proto::Action::kActivateTab: {
const ActivateTabAction& activate_tab_action = action.activate_tab();
return CreateActivateTabRequest(activate_tab_action,
deprecated_fallback_tab);
}
case optimization_guide::proto::Action::kAttemptLogin: {
const AttemptLoginAction& attempt_login_action = action.attempt_login();
return CreateAttemptLoginRequest(attempt_login_action,
deprecated_fallback_tab);
}
case optimization_guide::proto::Action::kScriptTool: {
const ScriptToolAction& script_tool_action = action.script_tool();
return CreateScriptToolRequest(script_tool_action,
deprecated_fallback_tab);
}
case optimization_guide::proto::Action::kCreateWindow:
case optimization_guide::proto::Action::kCloseWindow:
case optimization_guide::proto::Action::kActivateWindow:
case optimization_guide::proto::Action::kYieldToUser:
NOTIMPLEMENTED();
break;
case optimization_guide::proto::Action::ACTION_NOT_SET:
ACTOR_LOG() << "Action Type Not Set!";
break;
default:
NOTIMPLEMENTED();
break;
}
return nullptr;
}
base::expected<std::vector<std::unique_ptr<ToolRequest>>, size_t>
BuildToolRequest(const optimization_guide::proto::Actions& actions) {
std::vector<std::unique_ptr<ToolRequest>> requests;
requests.reserve(actions.actions_size());
for (int i = 0; i < actions.actions_size(); ++i) {
std::unique_ptr<actor::ToolRequest> request = actor::CreateToolRequest(
actions.actions().at(i), /*deprecated_fallback_tab=*/nullptr);
if (request) {
requests.push_back(std::move(request));
} else {
return base::unexpected(base::checked_cast<size_t>(i));
}
}
return requests;
}
apc::TabObservation ConvertToTabObservation(
const page_content_annotations::FetchPageContextResult& fetch_result) {
apc::TabObservation tab_observation;
if (fetch_result.screenshot_result) {
auto& data = fetch_result.screenshot_result->jpeg_data;
if (data.size() != 0) {
tab_observation.set_screenshot_mime_type(kMimeTypeJpeg);
// TODO(bokan): Can we avoid a copy here?
tab_observation.set_screenshot(data.data(), data.size());
}
}
if (fetch_result.annotated_page_content_result) {
*tab_observation.mutable_annotated_page_content() =
fetch_result.annotated_page_content_result->proto;
}
return tab_observation;
}
namespace {
void FetchCallback(base::RepeatingClosure barrier,
apc::TabObservation* tab_observation,
std::vector<optimization_guide::proto::ScriptToolResult>
script_tool_results,
ActorKeyedService::TabObservationResult result) {
base::ScopedClosureRunner run_barrier_at_return(barrier);
if (!result.has_value()) {
// TODO(crbug.com/435210098): There should be some way to message failure to
// observe.
return;
}
FetchPageContextResult& fetch_result = **result;
// RequestTabObservation should return an error if these aren't filled in.
CHECK(fetch_result.screenshot_result.has_value());
CHECK(fetch_result.annotated_page_content_result.has_value());
// TODO(khushalsagar): Remove this once consumers use ActionResults for script
// tool results.
CopyScriptToolResults(*fetch_result.annotated_page_content_result->proto
.mutable_main_frame_data(),
script_tool_results);
*tab_observation = ConvertToTabObservation(fetch_result);
}
} // namespace
void BuildActionsResultWithObservations(
content::BrowserContext& browser_context,
mojom::ActionResultCode result_code,
std::optional<size_t> index_of_failed_action,
std::vector<optimization_guide::proto::ScriptToolResult>
script_tool_results,
const ActorTask& task,
base::OnceCallback<void(std::unique_ptr<apc::ActionsResult>)> callback) {
auto response = std::make_unique<apc::ActionsResult>();
response->set_action_result(static_cast<int32_t>(result_code));
if (index_of_failed_action) {
response->set_index_of_failed_action(*index_of_failed_action);
}
CopyScriptToolResults(*response, script_tool_results);
auto* profile = Profile::FromBrowserContext(&browser_context);
std::vector<Browser*> browsers = chrome::FindAllTabbedBrowsersWithProfile(
profile, /*ignore_closing_browsers=*/true);
for (Browser* browser : browsers) {
apc::WindowObservation* window_observation = response->add_windows();
window_observation->set_id(browser->session_id().id());
window_observation->set_active(browser->IsActive());
if (tabs::TabInterface* tab = browser->GetActiveTabInterface()) {
window_observation->set_activated_tab_id(tab->GetHandle().raw_value());
}
for (const tabs::TabInterface* tab : *browser->GetTabStripModel()) {
window_observation->add_tab_ids(tab->GetHandle().raw_value());
}
}
absl::flat_hash_set<tabs::TabInterface*> tabs_to_fetch;
for (const tabs::TabHandle& handle : task.GetLastActedTabs()) {
// Include a TabObservation entry for acted on tabs. If the tab no longer
// exists or the fetch context failed, the observation will be empty.
// TODO(crbug.com/392167142): Check for a crashed tab here.
// TODO(crbug.com/434263095): We should probably avoid capturing
// observations if an action fails with kUrlBlocked. That might be better
// implemented by not putting the tab into the LastActedTabs set.
TabInterface* tab = handle.Get();
if (!tab) {
// TODO(crbug.com/435210098): There should be some way to message failure
// to capture an observation to the model (here and in FetchCallback). For
// now we leave the observation empty.
apc::TabObservation* tab_observation = response->add_tabs();
tab_observation->set_id(handle.raw_value());
} else {
tabs_to_fetch.insert(tab);
}
}
apc::ActionsResult* raw_response = response.get();
base::RepeatingClosure barrier = base::BarrierClosure(
tabs_to_fetch.size(),
base::BindOnce(std::move(callback), std::move(response)));
auto* actor_service = actor::ActorKeyedService::Get(profile);
CHECK(actor_service);
for (tabs::TabInterface* tab : tabs_to_fetch) {
apc::TabObservation* tab_observation = raw_response->add_tabs();
tab_observation->set_id(tab->GetHandle().raw_value());
// tab_observation can be Unretained because the underlying APC is owned by
// the barrier which is ref-counted.
actor_service->RequestTabObservation(
*tab, task.id(),
base::BindOnce(FetchCallback, barrier,
base::Unretained(tab_observation), script_tool_results));
}
}
apc::ActionsResult BuildErrorActionsResult(
mojom::ActionResultCode result_code,
std::optional<size_t> index_of_failed_action) {
apc::ActionsResult response;
CHECK(!IsOk(result_code));
response.set_action_result(static_cast<int32_t>(result_code));
if (index_of_failed_action) {
response.set_index_of_failed_action(*index_of_failed_action);
}
return response;
}
base::expected<std::vector<std::unique_ptr<ToolRequest>>, size_t>
BuildToolRequest(const optimization_guide::proto::BrowserAction& actions,
tabs::TabInterface* deprecated_fallback_tab) {
std::vector<std::unique_ptr<actor::ToolRequest>> requests;
requests.reserve(actions.actions_size());
for (int i = 0; i < actions.actions_size(); ++i) {
std::unique_ptr<actor::ToolRequest> request = actor::CreateToolRequest(
actions.actions().at(i), deprecated_fallback_tab);
if (request) {
requests.push_back(std::move(request));
} else {
return base::unexpected(base::checked_cast<size_t>(i));
}
}
return requests;
}
optimization_guide::proto::BrowserActionResult BuildBrowserActionResult(
mojom::ActionResultCode result_code,
int32_t tab_id) {
optimization_guide::proto::BrowserActionResult response;
response.set_action_result(static_cast<int32_t>(result_code));
response.set_tab_id(tab_id);
return response;
}
std::string ToBase64(const optimization_guide::proto::BrowserAction& actions) {
size_t size = actions.ByteSizeLong();
std::vector<uint8_t> buffer(size);
actions.SerializeToArray(buffer.data(), size);
return base::Base64Encode(buffer);
}
std::string ToBase64(const optimization_guide::proto::Actions& actions) {
size_t size = actions.ByteSizeLong();
std::vector<uint8_t> buffer(size);
actions.SerializeToArray(buffer.data(), size);
return base::Base64Encode(buffer);
}
} // namespace actor