blob: 5c981dae51a91662fc7b13a654b3e10deb5b3837 [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/tab_management_tool.h"
#include "base/functional/callback_forward.h"
#include "base/notimplemented.h"
#include "chrome/browser/actor/actor_task.h"
#include "chrome/browser/actor/tools/observation_delay_controller.h"
#include "chrome/browser/actor/tools/tool_callbacks.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "chrome/common/actor.mojom.h"
#include "chrome/common/actor/action_result.h"
#include "components/sessions/core/session_id.h"
#include "components/tabs/public/tab_interface.h"
#include "content/public/browser/page_navigator.h"
#include "content/public/browser/web_contents.h"
#include "third_party/abseil-cpp/absl/strings/str_format.h"
#include "ui/base/window_open_disposition.h"
#include "url/gurl.h"
#include "url/url_constants.h"
namespace actor {
using ::tabs::TabHandle;
TabManagementTool::TabManagementTool(TaskId task_id,
ToolDelegate& tool_delegate,
int32_t window_id,
WindowOpenDisposition create_disposition)
: Tool(task_id, tool_delegate),
action_(Action::kCreate),
create_disposition_(create_disposition),
window_id_(window_id) {}
TabManagementTool::TabManagementTool(TaskId task_id,
ToolDelegate& tool_delegate,
Action action,
TabHandle tab_handle)
: Tool(task_id, tool_delegate), action_(action), target_tab_(tab_handle) {}
TabManagementTool::~TabManagementTool() = default;
void TabManagementTool::Validate(ValidateCallback callback) {
PostResponseTask(std::move(callback), MakeOkResult());
}
void TabManagementTool::Invoke(InvokeCallback callback) {
callback_ = std::move(callback);
// TODO(crbug.com/445993857): Only the create action is hooked up and
// implemented.
switch (action_) {
case kCreate: {
CHECK(window_id_.has_value());
CHECK(create_disposition_.has_value());
BrowserWindowInterface* browser_window_interface =
BrowserWindowInterface::FromSessionID(
SessionID::FromSerializedValue(window_id_.value()));
if (!browser_window_interface) {
PostResponseTask(std::move(callback_),
MakeResult(mojom::ActionResultCode::kWindowWentAway));
return;
}
// The observer is removed in the TabStripModelObserver's destructor.
browser_window_interface->GetTabStripModel()->AddObserver(this);
// Watch for the window going away as well so we don't wait indefinitely.
browser_did_close_subscription_ =
browser_window_interface->RegisterBrowserDidClose(base::BindRepeating(
&TabManagementTool::OnBrowserDidClose, base::Unretained(this)));
// Open a blank tab.
browser_window_interface->OpenGURL(GURL(url::kAboutBlankURL),
create_disposition_.value());
break;
}
case kActivate:
case kClose:
CHECK(target_tab_.has_value());
NOTIMPLEMENTED() << "ActivateTab and CloseTab not yet implemented";
PostResponseTask(std::move(callback_),
MakeResult(mojom::ActionResultCode::kError));
return;
}
}
std::string TabManagementTool::DebugString() const {
return absl::StrFormat("TabManagementTool:%s", JournalEvent().c_str());
}
std::string TabManagementTool::JournalEvent() const {
switch (action_) {
case kCreate:
return "CreateTab";
case kActivate:
return "ActivateTab";
case kClose:
return "CloseTab";
}
}
std::unique_ptr<ObservationDelayController>
TabManagementTool::GetObservationDelayer(
ObservationDelayController::PageStabilityConfig page_stability_config) {
if (action_ != kCreate) {
return nullptr;
}
return std::make_unique<ObservationDelayController>(task_id(), journal());
}
void TabManagementTool::UpdateTaskAfterInvoke(ActorTask& task,
mojom::ActionResultPtr result,
InvokeCallback callback) const {
if (action_ == kCreate && target_tab_) {
task.AddTab(*target_tab_, std::move(callback));
} else {
std::move(callback).Run(std::move(result));
}
}
tabs::TabHandle TabManagementTool::GetTargetTab() const {
return target_tab_.value_or(tabs::TabHandle::Null());
}
void TabManagementTool::OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) {
if (action_ != kCreate) {
return;
}
if (change.type() == TabStripModelChange::kInserted) {
if (callback_) {
CHECK_GT(change.GetInsert()->contents.size(), 0ul);
target_tab_ = change.GetInsert()->contents[0].tab->GetHandle();
PostResponseTask(std::move(callback_), MakeOkResult());
}
}
}
void TabManagementTool::OnBrowserDidClose(BrowserWindowInterface* browser) {
// If the window is destroyed in the interval after a create tab has been
// invoked but before the tab's been added, this ensures we don't hang waiting
// for the new tab.
CHECK(window_id_);
if (action_ == kCreate && callback_) {
PostResponseTask(std::move(callback_),
MakeResult(mojom::ActionResultCode::kWindowWentAway));
}
}
} // namespace actor