blob: 69f1cd5dba25c6749565de867ce8fff3f3cedf9a [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/tools/navigate_tool.h"
#include "chrome/browser/actor/actor_task.h"
#include "chrome/browser/actor/site_policy.h"
#include "chrome/browser/actor/tools/observation_delay_controller.h"
#include "chrome/browser/actor/tools/tool_callbacks.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/actor.mojom.h"
#include "chrome/common/actor/action_result.h"
#include "chrome/common/actor/journal_details_builder.h"
#include "components/tabs/public/tab_interface.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "third_party/abseil-cpp/absl/strings/str_format.h"
#include "url/gurl.h"
using content::NavigationHandle;
using content::WebContents;
using tabs::TabInterface;
namespace actor {
namespace {
mojom::ActionResultPtr MayActOnUrlToResult(bool may_act) {
return may_act ? MakeOkResult()
: MakeResult(mojom::ActionResultCode::kUrlBlocked);
}
} // namespace
NavigateTool::NavigateTool(TaskId task_id,
ToolDelegate& tool_delegate,
TabInterface& tab,
const GURL& url)
: Tool(task_id, tool_delegate),
WebContentsObserver(tab.GetContents()),
url_(url),
tab_handle_(tab.GetHandle()) {}
NavigateTool::~NavigateTool() = default;
void NavigateTool::Validate(ValidateCallback callback) {
if (!url_.is_valid()) {
// URL is invalid.
PostResponseTask(std::move(callback),
MakeResult(mojom::ActionResultCode::kNavigateInvalidUrl));
return;
}
MayActOnUrl(url_,
/*allow_insecure_http=*/true,
Profile::FromBrowserContext(web_contents()->GetBrowserContext()),
journal(), task_id(),
base::BindOnce(&MayActOnUrlToResult).Then(std::move(callback)));
}
void NavigateTool::Invoke(InvokeCallback callback) {
content::OpenURLParams params(
url_, content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
::ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL,
false /* is_renderer_initiated */);
CHECK(web_contents());
invoke_callback_ = std::move(callback);
// TODO(crbug.com/406545255): If the page has a BeforeUnload handler the user
// may be prompted to confirm/abort the navigation, what should we do in those
// cases?
web_contents()->OpenURL(
params, base::BindOnce(&NavigateTool::NavigationHandleCallback,
weak_ptr_factory_.GetWeakPtr()));
}
std::string NavigateTool::DebugString() const {
return absl::StrFormat("NavigateTool[%s]", url_.spec());
}
std::string NavigateTool::JournalEvent() const {
return "Navigate";
}
std::unique_ptr<ObservationDelayController> NavigateTool::GetObservationDelayer(
ObservationDelayController::PageStabilityConfig page_stability_config) {
return std::make_unique<ObservationDelayController>(
*web_contents()->GetPrimaryMainFrame(), task_id(), journal(),
page_stability_config);
}
void NavigateTool::UpdateTaskBeforeInvoke(ActorTask& task,
InvokeCallback callback) const {
task.AddTab(tab_handle_, std::move(callback));
}
tabs::TabHandle NavigateTool::GetTargetTab() const {
return tab_handle_;
}
void NavigateTool::DidFinishNavigation(NavigationHandle* navigation_handle) {
if (pending_navigation_handle_id_ &&
navigation_handle->GetNavigationId() == *pending_navigation_handle_id_) {
journal().Log(url_, task_id(), "NavigateTool::DidFinishNavigation",
JournalDetailsBuilder()
.Add("id", navigation_handle->GetNavigationId())
.Build());
auto result =
navigation_handle->HasCommitted() && !navigation_handle->IsErrorPage()
? MakeOkResult()
: MakeErrorResult();
if (invoke_callback_) {
PostResponseTask(std::move(invoke_callback_), std::move(result));
return;
}
}
}
void NavigateTool::NavigationHandleCallback(NavigationHandle& handle) {
journal().Log(
url_, task_id(), "NavigateTool::NavigationHandleCallback",
JournalDetailsBuilder().Add("id", handle.GetNavigationId()).Build());
pending_navigation_handle_id_ = handle.GetNavigationId();
}
} // namespace actor