#include "chrome/browser/contextual_tasks/contextual_tasks_page_handler.h"
#include "base/check_deref.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/uuid.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/contextual_tasks/ai_mode_context_library_converter.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_ui.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_ui_service.h"
#include "chrome/browser/global_features.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/webui/webui_embedding_context.h"
#include "components/application_locale_storage/application_locale_storage.h"
#include "components/contextual_tasks/public/context_decoration_params.h"
#include "components/contextual_tasks/public/contextual_task.h"
#include "components/contextual_tasks/public/contextual_task_context.h"
#include "components/contextual_tasks/public/features.h"
#include "components/contextual_tasks/public/prefs.h"
#include "components/lens/lens_url_utils.h"
#include "components/prefs/pref_service.h"
#include "components/tabs/public/tab_interface.h"
#include "content/public/browser/web_ui.h"
#include "google_apis/gaia/gaia_constants.h"
#include "third_party/lens_server_proto/aim_communication.pb.h"
#include "url/gurl.h"
namespace {
constexpr char kMyActivityUrl[] = "https://myactivity.google.com/myactivity";
void OpenUrlInNewTab(content::WebUI* web_ui, const GURL& url) {
NavigateParams params(Profile::FromWebUI(web_ui), url,
ui::PAGE_TRANSITION_LINK);
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
Navigate(¶ms);
}
std::vector<contextual_tasks::mojom::TabPtr> TabsFromContext(
std::unique_ptr<contextual_tasks::ContextualTaskContext> context) {
if (!context) {
return {};
}
std::vector<contextual_tasks::mojom::TabPtr> tabs;
for (const auto& attachment : context->GetUrlAttachments()) {
auto tab = contextual_tasks::mojom::Tab::New();
tab->tab_id = attachment.GetTabSessionId().id();
tab->title = base::UTF16ToUTF8(attachment.GetTitle());
tab->url = attachment.GetURL();
tabs.push_back(std::move(tab));
}
return tabs;
}
}
ContextualTasksPageHandler::ContextualTasksPageHandler(
mojo::PendingReceiver<contextual_tasks::mojom::PageHandler> receiver,
ContextualTasksUI* web_ui_controller,
contextual_tasks::ContextualTasksUiService* ui_service,
contextual_tasks::ContextualTasksService* contextual_tasks_service)
: receiver_(this, std::move(receiver)),
web_ui_controller_(web_ui_controller),
ui_service_(ui_service),
contextual_tasks_service_(contextual_tasks_service) {
CHECK(contextual_tasks_service_);
contextual_tasks_service_observation_.Observe(contextual_tasks_service_);
}
ContextualTasksPageHandler::~ContextualTasksPageHandler() = default;
void ContextualTasksPageHandler::GetThreadUrl(GetThreadUrlCallback callback) {
std::move(callback).Run(ui_service_->GetDefaultAiPageUrl());
}
void ContextualTasksPageHandler::GetUrlForTask(const base::Uuid& uuid,
GetUrlForTaskCallback callback) {
std::optional<GURL> initial_url = ui_service_->GetInitialUrlForTask(uuid);
if (initial_url) {
std::move(callback).Run(initial_url.value());
return;
}
ui_service_->GetThreadUrlFromTaskId(
uuid, base::BindOnce([](GetUrlForTaskCallback callback,
GURL url) { std::move(callback).Run(url); },
std::move(callback)));
}
void ContextualTasksPageHandler::SetTaskId(const base::Uuid& uuid) {
web_ui_controller_->SetTaskId(uuid);
UpdateContextForTask(uuid);
}
void ContextualTasksPageHandler::SetThreadTitle(const std::string& title) {
web_ui_controller_->SetThreadTitle(title);
}
void ContextualTasksPageHandler::IsZeroState(const GURL& url,
IsZeroStateCallback callback) {
std::move(callback).Run(ContextualTasksUI::IsZeroState(url, ui_service_));
}
void ContextualTasksPageHandler::CloseSidePanel() {
web_ui_controller_->CloseSidePanel();
}
void ContextualTasksPageHandler::ShowThreadHistory() {
lens::ClientToAimMessage message;
message.mutable_open_threads_view()->mutable_payload();
PostMessageToWebview(message);
}
void ContextualTasksPageHandler::IsShownInTab(IsShownInTabCallback callback) {
std::move(callback).Run(web_ui_controller_->IsShownInTab());
}
void ContextualTasksPageHandler::OpenMyActivityUi() {
OpenUrlInNewTab(web_ui_controller_->web_ui(), GURL(kMyActivityUrl));
}
void ContextualTasksPageHandler::OpenHelpUi() {
OpenUrlInNewTab(web_ui_controller_->web_ui(),
GURL(contextual_tasks::GetContextualTasksHelpUrl()));
}
void ContextualTasksPageHandler::OpenOnboardingHelpUi() {
OpenUrlInNewTab(
web_ui_controller_->web_ui(),
GURL(contextual_tasks::GetContextualTasksOnboardingTooltipHelpUrl()));
}
void ContextualTasksPageHandler::MoveTaskUiToNewTab() {
auto* browser = web_ui_controller_->GetBrowser();
const auto& task_id = web_ui_controller_->GetTaskId();
if (!task_id.has_value()) {
LOG(ERROR) << "Attempted to open in new tab with no valid task ID.";
return;
}
ui_service_->MoveTaskUiToNewTab(task_id.value(), browser,
web_ui_controller_->GetInnerFrameUrl());
}
void ContextualTasksPageHandler::OnTabClickedFromSourcesMenu(int32_t tab_id,
const GURL& url) {
if (ui_service_) {
ui_service_->OnTabClickedFromSourcesMenu(
tab_id, url,
webui::GetBrowserWindowInterface(
web_ui_controller_->web_ui()->GetWebContents()));
}
}
void ContextualTasksPageHandler::OnWebviewMessage(
const std::vector<uint8_t>& message) {
lens::AimToClientMessage aim_to_client_message;
if (!aim_to_client_message.ParseFromArray(message.data(), message.size())) {
return;
}
if (aim_to_client_message.has_handshake_response()) {
web_ui_controller_->page()->OnHandshakeComplete();
web_ui_controller_->OnSidePanelStateChanged();
} else if (aim_to_client_message.has_hide_input()) {
web_ui_controller_->page()->HideInput();
} else if (aim_to_client_message.has_restore_input()) {
web_ui_controller_->page()->RestoreInput();
} else if (aim_to_client_message.has_enter_basic_mode()) {
web_ui_controller_->page()->HideInput();
} else if (aim_to_client_message.has_exit_basic_mode()) {
web_ui_controller_->page()->RestoreInput();
} else if (aim_to_client_message.has_update_thread_context_library()) {
OnReceivedUpdatedThreadContextLibrary(
aim_to_client_message.update_thread_context_library());
}
}
void ContextualTasksPageHandler::GetCommonSearchParams(
bool is_dark_mode,
bool is_side_panel,
GetCommonSearchParamsCallback callback) {
if (contextual_tasks::ShouldForceGscInTabMode()) {
is_side_panel = true;
}
std::string country_code =
g_browser_process->GetFeatures()->application_locale_storage()->Get();
if (contextual_tasks::ShouldForceCountryCodeUS()) {
country_code = "US";
}
auto params = lens::GetCommonSearchParametersMap(country_code, is_dark_mode,
is_side_panel);
if (contextual_tasks::ShouldForceCountryCodeUS()) {
params["gl"] = "us";
}
std::move(callback).Run(
base::flat_map<std::string, std::string>(params.begin(), params.end()));
}
void ContextualTasksPageHandler::OnboardingTooltipDismissed() {
if (!web_ui_controller_->web_ui()) {
return;
}
Profile* profile = Profile::FromWebUI(web_ui_controller_->web_ui());
if (!profile) {
return;
}
PrefService* prefs = profile->GetPrefs();
if (!prefs) {
return;
}
int count = prefs->GetInteger(
contextual_tasks::kContextualTasksOnboardingTooltipDismissedCount);
prefs->SetInteger(
contextual_tasks::kContextualTasksOnboardingTooltipDismissedCount,
count + 1);
}
void ContextualTasksPageHandler::PostMessageToWebview(
const lens::ClientToAimMessage& message) {
DCHECK(web_ui_controller_->page());
if (!web_ui_controller_->page()) {
return;
}
const size_t size = message.ByteSizeLong();
if (size == 0) {
LOG(WARNING) << "PostMessageToWebview called with an empty message.";
return;
}
std::vector<uint8_t> serialized_message(size);
if (!message.SerializeToArray(&serialized_message[0], size)) {
LOG(ERROR) << "Failed to serialize ClientToAimMessage.";
return;
}
web_ui_controller_->page()->PostMessageToWebview(serialized_message);
}
void ContextualTasksPageHandler::OnTaskUpdated(
const contextual_tasks::ContextualTask& task,
contextual_tasks::ContextualTasksService::TriggerSource source) {
if (!web_ui_controller_->page()) {
return;
}
const auto& current_task_id = web_ui_controller_->GetTaskId();
if (current_task_id != task.GetTaskId()) {
return;
}
UpdateContextForTask(task.GetTaskId());
}
void ContextualTasksPageHandler::UpdateContextForTask(
const base::Uuid& task_id) {
if (!base::FeatureList::IsEnabled(
contextual_tasks::kContextualTasksContextLibrary)) {
web_ui_controller_->page()->OnContextUpdated({});
return;
}
contextual_tasks_service_->GetContextForTask(
task_id, {contextual_tasks::ContextualTaskContextSource::kTabStrip},
std::make_unique<contextual_tasks::ContextDecorationParams>(),
base::BindOnce(
[](base::WeakPtr<ContextualTasksPageHandler> self,
std::unique_ptr<contextual_tasks::ContextualTaskContext> context) {
if (self && self->web_ui_controller_->page()) {
self->web_ui_controller_->page()->OnContextUpdated(
TabsFromContext(std::move(context)));
}
},
weak_ptr_factory_.GetWeakPtr()));
}
void ContextualTasksPageHandler::OnReceivedUpdatedThreadContextLibrary(
const lens::UpdateThreadContextLibrary& message) {
if (!base::FeatureList::IsEnabled(
contextual_tasks::kContextualTasksContextLibrary)) {
return;
}
const auto& task_id = web_ui_controller_->GetTaskId();
if (!task_id.has_value()) {
return;
}
contextual_search::ContextualSearchSessionHandle* handle =
web_ui_controller_->GetOrCreateContextualSessionHandle();
std::vector<contextual_search::FileInfo> submitted_context;
if (handle) {
submitted_context = handle->GetSubmittedContextFileInfos();
handle->ClearSubmittedContextTokens();
}
std::vector<contextual_tasks::UrlResource> committed_context =
contextual_tasks::ConvertAiModeContextToUrlResources(message,
submitted_context);
contextual_tasks_service_->SetUrlResourcesFromServer(*task_id,
committed_context);
}