blob: f0c4522a27bf90f8626c9e25f008bd9c4db8d345 [file] [log] [blame]
// Copyright 2024 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/ui/views/data_sharing/data_sharing_utils.h"
#include <optional>
#include <string>
#include <variant>
#include "base/functional/bind.h"
#include "base/notreached.h"
#include "base/strings/utf_string_conversions.h"
#include "base/token.h"
#include "chrome/browser/collaboration/collaboration_service_factory.h"
#include "chrome/browser/data_sharing/data_sharing_service_factory.h"
#include "chrome/browser/tab_group_sync/tab_group_sync_service_factory.h"
#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/collaboration/public/collaboration_service.h"
#include "components/collaboration/public/service_status.h"
#include "components/data_sharing/public/group_data.h"
#include "components/saved_tab_groups/public/saved_tab_group_tab.h"
#include "components/saved_tab_groups/public/tab_group_sync_service.h"
#include "components/saved_tab_groups/public/types.h"
#include "components/tab_groups/tab_group_id.h"
#include "components/url_formatter/elide_url.h"
#include "mojo/public/mojom/base/absl_status.mojom.h"
#include "net/base/url_util.h"
#include "ui/base/l10n/l10n_util.h"
namespace data_sharing {
RequestInfo::RequestInfo(
std::variant<tab_groups::LocalTabGroupID, data_sharing::GroupToken> id,
FlowType type)
: id(id), type(type) {}
RequestInfo::RequestInfo() : id(GroupToken()) {}
RequestInfo::RequestInfo(const RequestInfo& other) = default;
RequestInfo::~RequestInfo() = default;
GURL CreateShareUrl(const GURL& url,
const std::variant<tab_groups::LocalTabGroupID,
data_sharing::GroupToken>& group_id) {
GURL updated_url = url;
if (std::holds_alternative<tab_groups::LocalTabGroupID>(group_id)) {
tab_groups::TabGroupId local_group_id = std::get<0>(group_id);
// Return share flow url which requires a local group id to later
// associate with the collaboration_id returned by WebUI.
updated_url =
net::AppendQueryParameter(updated_url, kQueryParamFlow, kFlowShare);
updated_url = net::AppendQueryParameter(updated_url, kQueryParamTabGroupId,
local_group_id.ToString());
} else {
NOTREACHED();
}
return updated_url;
}
GURL CreateJoinUrl(const GURL& url,
const std::variant<tab_groups::LocalTabGroupID,
data_sharing::GroupToken>& group_id) {
GURL updated_url = url;
if (std::holds_alternative<tab_groups::LocalTabGroupID>(group_id)) {
NOTREACHED();
} else {
// Return join flow url which requires both collaboration_id and
// access_token for WebUI to fetch people info.
GroupToken group_token = std::get<1>(group_id);
CHECK(group_token.IsValid());
updated_url =
net::AppendQueryParameter(updated_url, kQueryParamFlow, kFlowJoin);
updated_url = net::AppendQueryParameter(updated_url, kQueryParamGroupId,
group_token.group_id.value());
updated_url = net::AppendQueryParameter(updated_url, kQueryParamTokenSecret,
group_token.access_token);
}
return updated_url;
}
GURL CreateManageUrl(const GURL& url,
const std::variant<tab_groups::LocalTabGroupID,
data_sharing::GroupToken>& group_id,
const std::optional<tab_groups::SavedTabGroup> saved_group,
bool is_disabled_for_policy) {
GURL updated_url = url;
CHECK(saved_group->is_shared_tab_group());
if (std::holds_alternative<tab_groups::LocalTabGroupID>(group_id)) {
// Return manage flow url which requires a group_id for webui to fetch
// people info.
updated_url =
net::AppendQueryParameter(updated_url, kQueryParamFlow, kFlowManage);
updated_url =
net::AppendQueryParameter(updated_url, kQueryParamGroupId,
saved_group->collaboration_id()->value());
tab_groups::TabGroupId local_group_id = std::get<0>(group_id);
updated_url = net::AppendQueryParameter(updated_url, kQueryParamTabGroupId,
local_group_id.ToString());
} else {
// Return manage flow url which requires a group_id for webui to fetch
// people info.
GroupToken group_token = std::get<1>(group_id);
updated_url =
net::AppendQueryParameter(updated_url, kQueryParamFlow, kFlowManage);
updated_url = net::AppendQueryParameter(updated_url, kQueryParamGroupId,
group_token.group_id.value());
}
updated_url =
net::AppendQueryParameter(updated_url, kQueryParamIsDisabledForPolicy,
is_disabled_for_policy ? kTrue : kFalse);
return updated_url;
}
GURL CreateLeaveUrl(const GURL& url,
const std::variant<tab_groups::LocalTabGroupID,
data_sharing::GroupToken>& group_id) {
GURL updated_url = url;
if (std::holds_alternative<tab_groups::LocalTabGroupID>(group_id)) {
NOTREACHED();
} else {
// Return manage flow url which requires a group_id for webui to fetch
// people info.
GroupToken group_token = std::get<1>(group_id);
updated_url =
net::AppendQueryParameter(updated_url, kQueryParamFlow, kFlowLeave);
updated_url = net::AppendQueryParameter(updated_url, kQueryParamGroupId,
group_token.group_id.value());
}
return updated_url;
}
GURL CreateDeleteUrl(
const GURL& url,
const std::optional<tab_groups::SavedTabGroup> saved_group) {
CHECK(saved_group);
CHECK(saved_group->collaboration_id());
GURL updated_url = url;
// Both variants will use the collaboration id from the saved_group.
updated_url =
net::AppendQueryParameter(updated_url, kQueryParamFlow, kFlowDelete);
updated_url =
net::AppendQueryParameter(updated_url, kQueryParamGroupId,
saved_group->collaboration_id()->value());
return updated_url;
}
GURL CreateCloseUrl(
const GURL& url,
const std::optional<tab_groups::SavedTabGroup> saved_group) {
CHECK(saved_group);
CHECK(saved_group->collaboration_id());
GURL updated_url = url;
// Both variants will use the collaboration id from the saved_group.
updated_url =
net::AppendQueryParameter(updated_url, kQueryParamFlow, kFlowClose);
updated_url =
net::AppendQueryParameter(updated_url, kQueryParamGroupId,
saved_group->collaboration_id()->value());
return updated_url;
}
} // namespace data_sharing
std::optional<GURL> data_sharing::GenerateWebUIUrl(RequestInfo request_info,
Profile* profile) {
tab_groups::TabGroupSyncService* const tab_group_service =
tab_groups::TabGroupSyncServiceFactory::GetForProfile(profile);
if (!tab_group_service) {
return std::nullopt;
}
// Find the saved group for the request.
std::optional<tab_groups::SavedTabGroup> saved_group = std::nullopt;
if (std::holds_alternative<tab_groups::LocalTabGroupID>(request_info.id)) {
saved_group = tab_group_service->GetGroup(std::get<0>(request_info.id));
if (!saved_group) {
// Local requests must be associated with a SavedTabGroup.
return std::nullopt;
}
} else {
// It's okay if `saved_group` is nullopt here since we could be joining a
// group we don't have yet.
GroupToken group_token = std::get<1>(request_info.id);
for (const tab_groups::SavedTabGroup& group :
tab_group_service->GetAllGroups()) {
if (group.collaboration_id() &&
group.collaboration_id()->value() == group_token.group_id.value()) {
saved_group = group;
break;
}
}
}
GURL url = GURL(chrome::kChromeUIUntrustedDataSharingURL);
switch (request_info.type) {
case kShare: {
url = CreateShareUrl(url, request_info.id);
break;
}
case kJoin: {
url = CreateJoinUrl(url, request_info.id);
break;
}
case kManage: {
auto* collaboration_service =
collaboration::CollaborationServiceFactory::GetForProfile(profile);
bool is_disabled_for_policy =
collaboration_service->GetServiceStatus().collaboration_status ==
collaboration::CollaborationStatus::kDisabledForPolicy;
url = CreateManageUrl(url, request_info.id, saved_group,
is_disabled_for_policy);
break;
}
case kDelete: {
if (!saved_group || !saved_group->collaboration_id()) {
// A shared group must exist for us to delete it.
return std::nullopt;
}
url = CreateDeleteUrl(url, saved_group);
break;
}
case kLeave:
if (!saved_group || !saved_group->collaboration_id()) {
// A shared group must exist for us to leave it.
return std::nullopt;
}
url = CreateLeaveUrl(url, request_info.id);
break;
case kClose:
if (!saved_group || !saved_group->collaboration_id()) {
// A shared group must exist for us to close it.
return std::nullopt;
}
url = CreateCloseUrl(url, saved_group);
break;
}
if (saved_group && request_info.type != kJoin) {
// If group is unnamed use default name e.g. "1 tab" / "3 tabs".
std::string title =
saved_group->title().empty()
? l10n_util::GetPluralStringFUTF8(IDS_SAVED_TAB_GROUP_TABS_COUNT,
saved_group->saved_tabs().size())
: base::UTF16ToUTF8(saved_group->title());
url = net::AppendQueryParameter(url, kQueryParamTabGroupTitle, title);
}
return std::make_optional(url);
}
GURL data_sharing::GetShareLink(const std::string& group_id,
const std::string& access_token,
Profile* profile) {
data_sharing::GroupData group_data;
group_data.group_token =
data_sharing::GroupToken(data_sharing::GroupId(group_id), access_token);
auto* data_sharing_service =
data_sharing::DataSharingServiceFactory::GetForProfile(profile);
// `group_id` and `access_token` are served by webui and should never be null
// when they get here. So the sharing url must be valid.
std::unique_ptr<GURL> url_ptr =
data_sharing_service->GetDataSharingUrl(group_data);
CHECK(url_ptr);
return *url_ptr;
}
void data_sharing::ProcessPreviewOutcome(
data_sharing::mojom::PageHandler::GetTabGroupPreviewCallback callback,
const data_sharing::DataSharingService::SharedDataPreviewOrFailureOutcome&
outcome) {
data_sharing::mojom::GroupPreviewPtr group_preview =
data_sharing::mojom::GroupPreview::New();
if (outcome.has_value()) {
if (outcome->shared_tab_group_preview) {
group_preview->title = outcome->shared_tab_group_preview->title;
for (const auto& tab : outcome->shared_tab_group_preview->tabs) {
group_preview->shared_tabs.push_back(
data_sharing::mojom::SharedTab::New(tab.GetDisplayUrl(), tab.url));
}
}
// If group is unnamed use default name e.g. "1 tab" / "3 tabs".
if (group_preview->title.empty()) {
group_preview->title = l10n_util::GetPluralStringFUTF8(
IDS_SAVED_TAB_GROUP_TABS_COUNT, group_preview->shared_tabs.size());
}
group_preview->status_code = mojo_base::mojom::AbslStatusCode::kOk;
} else {
// TODO(crbug.com/368634445): Convert returned PeopleGroupActionFailure to
// mojom::AbslStatusCode and let WebUI handle the errors.
group_preview->status_code = mojo_base::mojom::AbslStatusCode::kUnknown;
}
std::move(callback).Run(std::move(group_preview));
}
void data_sharing::GetTabGroupPreview(
const std::string& group_id,
const std::string& access_token,
Profile* profile,
data_sharing::mojom::PageHandler::GetTabGroupPreviewCallback callback) {
data_sharing::DataSharingService* data_sharing_service =
data_sharing::DataSharingServiceFactory::GetForProfile(profile);
auto group_token =
data_sharing::GroupToken(data_sharing::GroupId(group_id), access_token);
CHECK(group_token.IsValid());
data_sharing_service->GetSharedEntitiesPreview(
group_token, base::BindOnce(&ProcessPreviewOutcome, std::move(callback)));
}