blob: b3d02479e61297dc9fb9a86283b3f028182bce77 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/clipboard/clipboard_history_util.h"
#include <array>
#include "ash/clipboard/clipboard_history_item.h"
#include "ash/metrics/histogram_macros.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/style/ash_color_provider.h"
#include "base/files/file_path.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/ui/base/file_icon_util.h"
#include "ui/base/clipboard/clipboard_data.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/gfx/paint_vector_icon.h"
namespace ash {
namespace ClipboardHistoryUtil {
namespace {
constexpr char16_t kFileSystemSourcesType[] = u"fs/sources";
// The array of formats in order of decreasing priority.
constexpr ui::ClipboardInternalFormat kPrioritizedFormats[] = {
ui::ClipboardInternalFormat::kPng,
ui::ClipboardInternalFormat::kHtml,
ui::ClipboardInternalFormat::kText,
ui::ClipboardInternalFormat::kRtf,
ui::ClipboardInternalFormat::kFilenames,
ui::ClipboardInternalFormat::kBookmark,
ui::ClipboardInternalFormat::kWeb,
ui::ClipboardInternalFormat::kCustom};
} // namespace
absl::optional<ui::ClipboardInternalFormat> CalculateMainFormat(
const ui::ClipboardData& data) {
for (const auto& format : kPrioritizedFormats) {
if (ContainsFormat(data, format)) {
return format;
}
}
return absl::nullopt;
}
ClipboardHistoryDisplayFormat CalculateDisplayFormat(
const ui::ClipboardData& data) {
switch (CalculateMainFormat(data).value()) {
case ui::ClipboardInternalFormat::kPng:
return ClipboardHistoryDisplayFormat::kPng;
case ui::ClipboardInternalFormat::kHtml:
if ((data.markup_data().find("<img") == std::string::npos) &&
(data.markup_data().find("<table") == std::string::npos)) {
return ClipboardHistoryDisplayFormat::kText;
}
return ClipboardHistoryDisplayFormat::kHtml;
case ui::ClipboardInternalFormat::kText:
case ui::ClipboardInternalFormat::kSvg:
case ui::ClipboardInternalFormat::kRtf:
case ui::ClipboardInternalFormat::kBookmark:
case ui::ClipboardInternalFormat::kWeb:
return ClipboardHistoryDisplayFormat::kText;
case ui::ClipboardInternalFormat::kFilenames:
return ClipboardHistoryDisplayFormat::kFile;
case ui::ClipboardInternalFormat::kCustom:
return ContainsFileSystemData(data)
? ClipboardHistoryDisplayFormat::kFile
: ClipboardHistoryDisplayFormat::kText;
}
}
bool ContainsFormat(const ui::ClipboardData& data,
ui::ClipboardInternalFormat format) {
return data.format() & static_cast<int>(format);
}
void RecordClipboardHistoryItemDeleted(const ClipboardHistoryItem& item) {
UMA_HISTOGRAM_ENUMERATION(
"Ash.ClipboardHistory.ContextMenu.DisplayFormatDeleted",
CalculateDisplayFormat(item.data()));
}
void RecordClipboardHistoryItemPasted(const ClipboardHistoryItem& item) {
UMA_HISTOGRAM_ENUMERATION(
"Ash.ClipboardHistory.ContextMenu.DisplayFormatPasted",
CalculateDisplayFormat(item.data()));
}
bool ContainsFileSystemData(const ui::ClipboardData& data) {
return !GetFileSystemSources(data).empty();
}
void GetSplitFileSystemData(const ui::ClipboardData& data,
std::vector<base::StringPiece16>* source_list,
std::u16string* sources) {
DCHECK(sources);
DCHECK(sources->empty());
DCHECK(source_list);
DCHECK(source_list->empty());
*sources = GetFileSystemSources(data);
if (sources->empty()) {
// Not a file system data.
return;
}
// Split sources into a list.
*source_list = base::SplitStringPiece(*sources, u"\n", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
}
size_t GetCountOfCopiedFiles(const ui::ClipboardData& data) {
std::u16string sources;
std::vector<base::StringPiece16> source_list;
GetSplitFileSystemData(data, &source_list, &sources);
if (sources.empty()) {
// Not a file system data.
return 0;
}
return source_list.size();
}
std::u16string GetFileSystemSources(const ui::ClipboardData& data) {
// Outside of the Files app, file system sources are written as filenames.
if (ContainsFormat(data, ui::ClipboardInternalFormat::kFilenames)) {
std::vector<std::string> sources;
for (const ui::FileInfo& filename : data.filenames())
sources.push_back(filename.path.value());
return base::UTF8ToUTF16(base::JoinString(sources, "\n"));
}
// Within the Files app, file system sources are written as custom data.
if (!ContainsFormat(data, ui::ClipboardInternalFormat::kCustom))
return std::u16string();
// Attempt to read file system sources in the custom data.
std::u16string sources;
ui::ReadCustomDataForType(data.custom_data_data().c_str(),
data.custom_data_data().size(),
kFileSystemSourcesType, &sources);
return sources;
}
bool IsSupported(const ui::ClipboardData& data) {
const absl::optional<ui::ClipboardInternalFormat> format =
CalculateMainFormat(data);
// Empty `data` is not supported.
if (!format.has_value())
return false;
// The only supported type of custom data is file system data.
if (format.value() == ui::ClipboardInternalFormat::kCustom)
return ContainsFileSystemData(data);
return true;
}
bool IsEnabledInCurrentMode() {
const auto* session_controller = Shell::Get()->session_controller();
// The clipboard history menu is enabled only when a user has logged in and
// login UI is hidden.
if (session_controller->GetSessionState() !=
session_manager::SessionState::ACTIVE) {
return false;
}
switch (session_controller->login_status()) {
case LoginStatus::NOT_LOGGED_IN:
case LoginStatus::LOCKED:
case LoginStatus::KIOSK_APP:
case LoginStatus::PUBLIC:
return false;
case LoginStatus::USER:
case LoginStatus::GUEST:
case LoginStatus::CHILD:
return true;
}
}
gfx::ImageSkia GetIconForFileClipboardItem(const ClipboardHistoryItem& item,
const std::string& file_name) {
DCHECK_EQ(ClipboardHistoryDisplayFormat::kFile,
CalculateDisplayFormat(item.data()));
const int copied_files_count = GetCountOfCopiedFiles(item.data());
if (copied_files_count == 0)
return gfx::ImageSkia();
if (copied_files_count == 1) {
return chromeos::GetIconForPath(
base::FilePath(file_name),
ash::AshColorProvider::Get()->IsDarkModeEnabled());
}
constexpr std::array<const gfx::VectorIcon*, 9> icons = {
&kTwoFilesIcon, &kThreeFilesIcon, &kFourFilesIcon,
&kFiveFilesIcon, &kSixFilesIcon, &kSevenFilesIcon,
&kEightFilesIcon, &kNineFilesIcon, &kMoreThanNineFilesIcon};
int icon_index = std::min(copied_files_count - 2, (int)icons.size() - 1);
const SkColor icon_color = ash::AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kIconColorPrimary);
return CreateVectorIcon(*icons[icon_index], icon_color);
}
} // namespace ClipboardHistoryUtil
} // namespace ash