// Copyright 2023 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/ash/policy/dlp/dialogs/files_policy_dialog.h"
#include <cstddef>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "ash/style/typography.h"
#include "base/files/file_path.h"
#include "chrome/browser/ash/policy/dlp/dialogs/files_policy_error_dialog.h"
#include "chrome/browser/ash/policy/dlp/dialogs/files_policy_warn_dialog.h"
#include "chrome/browser/ash/policy/dlp/files_policy_string_util.h"
#include "chrome/browser/chromeos/policy/dlp/dialogs/policy_dialog_base.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_confidential_file.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_file_destination.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/widget/widget.h"
namespace policy {
namespace {
// Returns the accessible name for the learn more link of the given block
// `reason`.
std::u16string GetAccessibleLearnMoreLinkNameForBlockReason(
policy::FilesPolicyDialog::BlockReason reason) {
switch (reason) {
case FilesPolicyDialog::BlockReason::kDlp:
return l10n_util::GetStringUTF16(
IDS_POLICY_DLP_FILES_LEARN_MORE_ABOUT_DATA_CONTROLS_ACCESSIBLE_NAME);
case FilesPolicyDialog::BlockReason::kEnterpriseConnectorsSensitiveData:
return l10n_util::GetStringUTF16(
IDS_POLICY_DLP_FILES_LEARN_MORE_ABOUT_SENSITIVE_DATA_PROTECTION_ACCESSIBLE_NAME);
case FilesPolicyDialog::BlockReason::kEnterpriseConnectorsMalware:
return l10n_util::GetStringUTF16(
IDS_POLICY_DLP_FILES_LEARN_MORE_ABOUT_MALWARE_PROTECTION_ACCESSIBLE_NAME);
case FilesPolicyDialog::BlockReason::kEnterpriseConnectorsScanFailed:
case FilesPolicyDialog::BlockReason::kEnterpriseConnectorsUnknownScanResult:
case FilesPolicyDialog::BlockReason::kEnterpriseConnectorsEncryptedFile:
case FilesPolicyDialog::BlockReason::kEnterpriseConnectorsLargeFile:
case FilesPolicyDialog::BlockReason::kEnterpriseConnectors:
// Currently these block reasons cannot have a learn more link.
return std::u16string();
}
}
} // namespace
FilesPolicyDialogFactory* factory_;
// static
PolicyDialogBase::ViewIds FilesPolicyDialog::MapBlockReasonToViewID(
BlockReason reason) {
switch (reason) {
case FilesPolicyDialog::BlockReason::kDlp:
return PolicyDialogBase::kDlpSectionId;
case FilesPolicyDialog::BlockReason::kEnterpriseConnectorsUnknownScanResult:
return PolicyDialogBase::kEnterpriseConnectorsUnknownScanResultSectionId;
case FilesPolicyDialog::BlockReason::kEnterpriseConnectorsScanFailed:
return PolicyDialogBase::kEnterpriseConnectorsScanFailedResultSectionId;
case FilesPolicyDialog::BlockReason::kEnterpriseConnectorsSensitiveData:
return PolicyDialogBase::kEnterpriseConnectorsSensitiveDataSectionId;
case FilesPolicyDialog::BlockReason::kEnterpriseConnectorsMalware:
return PolicyDialogBase::kEnterpriseConnectorsMalwareSectionId;
case FilesPolicyDialog::BlockReason::kEnterpriseConnectorsEncryptedFile:
return PolicyDialogBase::kEnterpriseConnectorsEncryptedFileSectionId;
case FilesPolicyDialog::BlockReason::kEnterpriseConnectorsLargeFile:
return PolicyDialogBase::kEnterpriseConnectorsLargeFileSectionId;
case FilesPolicyDialog::BlockReason::kEnterpriseConnectors:
return PolicyDialogBase::kEnterpriseConnectorsSectionId;
}
}
// static
FilesPolicyDialog::Info FilesPolicyDialog::Info::Warn(
BlockReason reason,
const std::vector<base::FilePath>& paths) {
CHECK(!paths.empty());
Info settings;
settings.files_ =
std::vector<DlpConfidentialFile>(paths.begin(), paths.end());
// TODO(b/300705572): we probably want to have a default message for every
// block reason.
int message_id = IDS_POLICY_DLP_FILES_WARN_MESSAGE;
settings.message_ = base::ReplaceStringPlaceholders(
l10n_util::GetPluralStringFUTF16(message_id, paths.size()),
base::NumberToString16(paths.size()),
/*offset=*/nullptr);
// Only DLP has a default learn more URL.
if (reason == FilesPolicyDialog::BlockReason::kDlp) {
settings.learn_more_url_ = GURL(dlp::kDlpLearnMoreUrl);
}
settings.accessible_learn_more_link_name_ =
GetAccessibleLearnMoreLinkNameForBlockReason(reason);
return settings;
}
// static
FilesPolicyDialog::Info FilesPolicyDialog::Info::Error(
BlockReason reason,
const std::vector<base::FilePath>& paths) {
CHECK(!paths.empty());
size_t file_count = paths.size();
Info settings;
settings.files_ =
std::vector<DlpConfidentialFile>(paths.begin(), paths.end());
settings.message_ =
files_string_util::GetBlockReasonMessage(reason, file_count);
// Only DLP has a default learn more URL.
if (reason == FilesPolicyDialog::BlockReason::kDlp) {
settings.learn_more_url_ = GURL(dlp::kDlpLearnMoreUrl);
}
settings.accessible_learn_more_link_name_ =
GetAccessibleLearnMoreLinkNameForBlockReason(reason);
return settings;
}
FilesPolicyDialog::Info::Info() = default;
FilesPolicyDialog::Info::~Info() = default;
FilesPolicyDialog::Info::Info(const Info& other) = default;
FilesPolicyDialog::Info& FilesPolicyDialog::Info::operator=(Info&& other) =
default;
bool FilesPolicyDialog::Info::operator==(const Info& other) const {
return bypass_requires_justification_ ==
other.bypass_requires_justification_ &&
message_ == other.message_ &&
learn_more_url_ == other.learn_more_url_ && files_ == other.files_ &&
accessible_learn_more_link_name_ ==
other.accessible_learn_more_link_name_;
}
bool FilesPolicyDialog::Info::operator!=(const Info& other) const {
return !(*this == other);
}
const std::vector<DlpConfidentialFile>& FilesPolicyDialog::Info::GetFiles()
const {
return files_;
}
bool FilesPolicyDialog::Info::DoesBypassRequireJustification() const {
return bypass_requires_justification_;
}
void FilesPolicyDialog::Info::SetBypassRequiresJustification(bool value) {
bypass_requires_justification_ = value;
}
std::u16string FilesPolicyDialog::Info::GetMessage() const {
return message_;
}
void FilesPolicyDialog::Info::SetMessage(
const std::optional<std::u16string>& message) {
if (message.has_value() && !message->empty()) {
message_ = l10n_util::GetStringFUTF16(
IDS_POLICY_DLP_FROM_YOUR_ADMIN_MESSAGE, message.value());
is_custom_message_ = true;
}
}
bool FilesPolicyDialog::Info::HasCustomMessage() const {
return is_custom_message_;
}
std::optional<GURL> FilesPolicyDialog::Info::GetLearnMoreURL() const {
return learn_more_url_;
}
void FilesPolicyDialog::Info::SetLearnMoreURL(const std::optional<GURL>& url) {
if (url.has_value() && url->is_valid()) {
learn_more_url_ = url.value();
}
}
std::u16string FilesPolicyDialog::Info::GetAccessibleLearnMoreLinkName() const {
return accessible_learn_more_link_name_;
}
bool FilesPolicyDialog::Info::HasCustomDetails() const {
return DoesBypassRequireJustification() || HasCustomMessage() ||
is_custom_learn_more_url_;
}
FilesPolicyDialog::FilesPolicyDialog(size_t file_count,
dlp::FileAction action,
gfx::NativeWindow modal_parent)
: action_(action), file_count_(file_count) {
ui::mojom::ModalType modal = modal_parent ? ui::mojom::ModalType::kWindow
: ui::mojom::ModalType::kSystem;
set_margins(gfx::Insets::TLBR(24, 0, 20, 0));
SetModalType(modal);
}
FilesPolicyDialog::~FilesPolicyDialog() = default;
views::Widget* FilesPolicyDialog::CreateWarnDialog(
WarningWithJustificationCallback callback,
dlp::FileAction action,
gfx::NativeWindow modal_parent,
Info dialog_info,
std::optional<DlpFileDestination> destination) {
if (factory_) {
return factory_->CreateWarnDialog(std::move(callback), action, modal_parent,
destination, std::move(dialog_info));
}
views::Widget* widget = views::DialogDelegate::CreateDialogWidget(
std::make_unique<FilesPolicyWarnDialog>(std::move(callback), action,
modal_parent, destination,
std::move(dialog_info)),
/*context=*/nullptr, /*parent=*/modal_parent);
widget->Show();
return widget;
}
views::Widget* FilesPolicyDialog::CreateErrorDialog(
const std::map<BlockReason, Info>& dialog_info_map,
dlp::FileAction action,
gfx::NativeWindow modal_parent) {
if (factory_) {
return factory_->CreateErrorDialog(dialog_info_map, action, modal_parent);
}
views::Widget* widget = views::DialogDelegate::CreateDialogWidget(
std::make_unique<FilesPolicyErrorDialog>(dialog_info_map, action,
modal_parent),
/*context=*/nullptr, /*parent=*/modal_parent);
widget->Show();
return widget;
}
// static
void FilesPolicyDialog::SetFactory(FilesPolicyDialogFactory* factory) {
delete factory_;
factory_ = factory;
}
void FilesPolicyDialog::SetupScrollView() {
// Call the parent class to setup the element. Do not remove.
PolicyDialogBase::SetupScrollView();
views::BoxLayout* layout = scroll_view_container_->SetLayoutManager(
std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical,
gfx::Insets::TLBR(8, 8, 8, 24),
/*between_child_spacing=*/0));
layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kStart);
}
views::Label* FilesPolicyDialog::AddTitle(const std::u16string& title) {
// Call the parent class to setup the element. Do not remove.
views::Label* title_label = PolicyDialogBase::AddTitle(title);
title_label->SetFontList(
ash::TypographyProvider::Get()->ResolveTypographyToken(
ash::TypographyToken::kCrosTitle1));
return title_label;
}
views::Label* FilesPolicyDialog::AddMessage(const std::u16string& message) {
if (message.empty()) {
// Some dialogs, like the mixed error dialogs don't have a single message,
// but add the error description inside the scrollable list, so skip adding
// the element altogether.
return nullptr;
}
// Call the parent class to setup the element. Do not remove.
views::Label* message_label = PolicyDialogBase::AddMessage(message);
message_label->SetFontList(
ash::TypographyProvider::Get()->ResolveTypographyToken(
ash::TypographyToken::kCrosBody1));
return message_label;
}
void FilesPolicyDialog::AddConfidentialRow(const gfx::ImageSkia& icon,
const std::u16string& title) {
DCHECK(scroll_view_container_);
views::View* row =
scroll_view_container_->AddChildView(std::make_unique<views::View>());
row->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal,
gfx::Insets::TLBR(10, 16, 10, 16), /*between_child_spacing=*/16));
AddRowIcon(icon, row);
views::Label* title_label = AddRowTitle(title, row);
title_label->SetID(PolicyDialogBase::kConfidentialRowTitleViewId);
title_label->SetMultiLine(false);
title_label->SetElideBehavior(gfx::ElideBehavior::FADE_TAIL);
title_label->SetFontList(
ash::TypographyProvider::Get()->ResolveTypographyToken(
ash::TypographyToken::kCrosBody1));
}
BEGIN_METADATA(FilesPolicyDialog)
END_METADATA
} // namespace policy