blob: 010cffc1c30ebc76d7c35978a961a2f83ca8b558 [file]
// 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/renderer/actor/script_tool.h"
#include <memory>
#include <optional>
#include "base/notimplemented.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/to_string.h"
#include "chrome/common/actor/action_result.h"
#include "chrome/common/actor/actor_constants.h"
#include "chrome/renderer/actor/tool_utils.h"
#include "components/actor/core/actor_logging.h"
#include "components/actor/public/mojom/actor_types.mojom.h"
#include "content/public/renderer/render_frame.h"
#include "third_party/abseil-cpp/absl/strings/str_format.h"
#include "third_party/blink/public/mojom/content_extraction/script_tools.mojom.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_script_tool_types.h"
#include "ui/events/base_event_utils.h"
namespace actor {
namespace {
mojom::ActionResultPtr OnToolExecuted(
const std::string& name,
const std::string& input_arguments,
std::unique_ptr<blink::WebScriptToolDeclaration> tool,
base::expected<blink::WebString, blink::WebScriptToolError> response) {
if (!response.has_value()) {
mojom::ActionResultCode code;
switch (response.error().code) {
case blink::WebScriptToolErrorCode::kInvalidToolName:
code = mojom::ActionResultCode::kScriptToolInvalidName;
break;
case blink::WebScriptToolErrorCode::kInvalidInputArguments:
code = mojom::ActionResultCode::kScriptToolInvalidInputArguments;
break;
case blink::WebScriptToolErrorCode::kMissingRequiredSubmitButton:
code = mojom::ActionResultCode::kScriptToolMissingRequiredSubmitButton;
break;
case blink::WebScriptToolErrorCode::kToolInvocationFailed:
code = mojom::ActionResultCode::kScriptToolInvocationFailed;
break;
case blink::WebScriptToolErrorCode::kToolCancelled:
code = mojom::ActionResultCode::kScriptToolCancelled;
break;
}
return MakeResult(code, /*requires_page_stabilization=*/false,
response.error().message.Utf8());
}
auto result = MakeOkResult();
auto script_tool_response = mojom::ScriptToolResponse::New();
script_tool_response->input_arguments = input_arguments;
script_tool_response->tool = blink::mojom::ScriptTool::New();
script_tool_response->tool->name = name;
script_tool_response->tool->description = tool->description.Utf8();
script_tool_response->tool->input_schema = tool->input_schema.Utf8();
if (tool->read_only.has_value() || tool->untrusted_content.has_value() ||
tool->consequential.has_value() || tool->debugging.has_value()) {
script_tool_response->tool->annotations =
blink::mojom::ScriptToolAnnotations::New();
if (tool->read_only.has_value()) {
script_tool_response->tool->annotations->read_only =
tool->read_only.value();
}
if (tool->untrusted_content.has_value()) {
script_tool_response->tool->annotations->untrusted_content =
tool->untrusted_content.value();
}
if (tool->consequential.has_value()) {
script_tool_response->tool->annotations->consequential =
tool->consequential.value();
}
if (tool->debugging.has_value()) {
script_tool_response->tool->annotations->debugging =
tool->debugging.value();
}
}
if (!response->IsEmpty()) {
script_tool_response->result = response->Utf8();
}
result->script_tool_response = std::move(script_tool_response);
return result;
}
} // namespace
ScriptTool::ScriptTool(content::RenderFrame& frame,
TaskId task_id,
base::UnguessableToken execution_id,
Journal& journal,
mojom::ToolTargetPtr target,
mojom::ObservedToolTargetPtr observed_target,
mojom::ScriptToolActionPtr action)
: ToolBase(frame,
task_id,
journal,
std::move(target),
std::move(observed_target)),
action_(std::move(action)),
execution_id_(execution_id) {}
ScriptTool::~ScriptTool() = default;
void ScriptTool::Execute(ToolFinishedCallback callback) {
frame_->GetWebFrame()->GetDocument().ExecuteScriptTool(
execution_id_.value(), blink::WebString::FromUtf8(action_->name),
blink::WebString::FromUtf8(action_->input_arguments),
base::BindOnce(&OnToolExecuted, action_->name, action_->input_arguments)
.Then(std::move(callback)));
}
void ScriptTool::Cancel() {
if (!execution_id_.has_value()) {
return;
}
// CancelScriptTool() synchronously dispatches DOM events that might destroy
// the owning frame and this tool. Use a weak pointer to detect if `this` is
// still valid.
base::WeakPtr<ScriptTool> weak_this = weak_ptr_factory_.GetWeakPtr();
frame_->GetWebFrame()->GetDocument().CancelScriptTool(execution_id_.value());
if (weak_this) {
execution_id_.reset();
}
}
std::string ScriptTool::DebugString() const {
return absl::StrFormat("ScriptTool[tool_name(%s);input_arguments(%s)]",
action_->name, action_->input_arguments);
}
ValidationResult ScriptTool::Validate() {
return ValidationResult(MakeOkResult());
}
bool ScriptTool::EnsureTargetInView() {
return false;
}
} // namespace actor