Commit 599ebd7d authored by David Black's avatar David Black Committed by Commit Bot

Parse custom data for file names when generating history label.

Clipboard history will now show the actual file names of files copied
from the File Manager.

This CL pulls logic out into ash::clipboard::helper to facilitate easier
testing.

Bug: 1108901
Change-Id: I3b66bb11b7bd0f0682e9870625ab724c643d37a4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2324557
Commit-Queue: David Black <dmblack@google.com>
Reviewed-by: default avatarAlex Newcomer <newcomer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#793377}
parent c9bd85b1
......@@ -257,6 +257,8 @@ component("ash") {
"clipboard/clipboard_history.h",
"clipboard/clipboard_history_controller.cc",
"clipboard/clipboard_history_controller.h",
"clipboard/clipboard_history_helper.cc",
"clipboard/clipboard_history_helper.h",
"clipboard/clipboard_history_menu_model_adapter.cc",
"clipboard/clipboard_history_menu_model_adapter.h",
"dbus/ash_dbus_services.cc",
......@@ -1759,6 +1761,7 @@ test("ash_unittests") {
"assistant/util/resource_util_unittest.cc",
"autoclick/autoclick_drag_event_rewriter_unittest.cc",
"autoclick/autoclick_unittest.cc",
"clipboard/clipboard_history_helper_unittest.cc",
"clipboard/clipboard_history_unittest.cc",
"dbus/gesture_properties_service_provider_unittest.cc",
"dbus/url_handler_service_provider_unittest.cc",
......
......@@ -6,6 +6,7 @@
#include "ash/accelerators/accelerator_controller_impl.h"
#include "ash/clipboard/clipboard_history.h"
#include "ash/clipboard/clipboard_history_helper.h"
#include "ash/clipboard/clipboard_history_menu_model_adapter.h"
#include "ash/public/cpp/window_tree_host_lookup.h"
#include "ash/resources/vector_icons/vector_icons.h"
......@@ -35,35 +36,7 @@ namespace ash {
namespace {
base::string16 GetLabelForClipboardData(const ui::ClipboardData& item) {
if (item.format() & static_cast<int>(ui::ClipboardInternalFormat::kBitmap)) {
return ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
IDS_CLIPBOARD_MENU_IMAGE);
}
if (item.format() & static_cast<int>(ui::ClipboardInternalFormat::kText))
return base::UTF8ToUTF16(item.text());
if (item.format() & static_cast<int>(ui::ClipboardInternalFormat::kHtml))
return base::UTF8ToUTF16(item.markup_data());
if (item.format() & static_cast<int>(ui::ClipboardInternalFormat::kRtf)) {
return ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
IDS_CLIPBOARD_MENU_RTF_CONTENT);
}
if (item.format() &
static_cast<int>(ui::ClipboardInternalFormat::kBookmark)) {
return base::UTF8ToUTF16(item.bookmark_title());
}
if (item.format() & static_cast<int>(ui::ClipboardInternalFormat::kWeb)) {
return ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
IDS_CLIPBOARD_MENU_WEB_SMART_PASTE);
}
if (item.format() & static_cast<int>(ui::ClipboardInternalFormat::kCustom)) {
// TODO(crbug/1108901): Handle file manager case.
// TODO(crbug/1108902): Handle fallback case.
return base::UTF8ToUTF16("<CUSTOM DATA>");
}
return base::string16();
}
// TODO(dmblack): Move to clipboard_history_helper.
ui::ImageModel GetImageModelForClipboardData(const ui::ClipboardData& item) {
if (item.format() & static_cast<int>(ui::ClipboardInternalFormat::kBitmap)) {
// TODO(newcomer): Show a smaller version of the bitmap.
......@@ -190,7 +163,7 @@ void ClipboardHistoryController::ShowMenu() {
IDS_CLIPBOARD_MENU_CLIPBOARD));
int index = 0;
for (const auto& item : clipboard_items_) {
menu_model->AddItemWithIcon(index++, GetLabelForClipboardData(item),
menu_model->AddItemWithIcon(index++, clipboard::helper::GetLabel(item),
GetImageModelForClipboardData(item));
}
menu_model->AddSeparator(ui::MenuSeparatorType::NORMAL_SEPARATOR);
......
// 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_helper.h"
#include <string>
#include <vector>
#include "base/notreached.h"
#include "base/strings/escape.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/base/clipboard/clipboard_data.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/strings/grit/ui_strings.h"
namespace ash {
namespace clipboard {
namespace helper {
namespace {
constexpr char kFileSystemSourcesType[] = "fs/sources";
// Private ---------------------------------------------------------------------
// Returns true if |data| contains the specified |format|.
bool ContainsFormat(const ui::ClipboardData& data,
ui::ClipboardInternalFormat format) {
return data.format() & static_cast<int>(format);
}
// Returns the localized string for the specified |resource_id|.
base::string16 GetLocalizedString(int resource_id) {
return ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
resource_id);
}
// TODO(crbug/1108902): Handle fallback case.
// Returns the label to display for the custom data contained within |data|.
base::string16 GetLabelForCustomData(const ui::ClipboardData& data) {
DCHECK(ContainsFormat(data, ui::ClipboardInternalFormat::kCustom));
// Attempt to read file system sources in the custom data.
base::string16 sources;
ui::ReadCustomDataForType(
data.custom_data_data().c_str(), data.custom_data_data().size(),
base::UTF8ToUTF16(kFileSystemSourcesType), &sources);
if (sources.empty())
return base::UTF8ToUTF16("<Custom Data>");
// Split sources into a list.
std::vector<base::StringPiece16> source_list =
base::SplitStringPiece(sources, base::UTF8ToUTF16("\n"),
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
// Strip path information, so all that's left are file names.
for (auto it = source_list.begin(); it != source_list.end(); ++it)
*it = it->substr(it->find_last_of(base::UTF8ToUTF16("/")) + 1);
// Join file names, unescaping encoded character sequences for display. This
// ensures that "My%20File.txt" will display as "My File.txt".
return base::UTF8ToUTF16(base::UnescapeURLComponent(
base::UTF16ToUTF8(base::JoinString(source_list, base::UTF8ToUTF16(", "))),
base::UnescapeRule::SPACES));
}
} // namespace
// Public ----------------------------------------------------------------------
base::string16 GetLabel(const ui::ClipboardData& data) {
if (ContainsFormat(data, ui::ClipboardInternalFormat::kBitmap))
return GetLocalizedString(IDS_CLIPBOARD_MENU_IMAGE);
if (ContainsFormat(data, ui::ClipboardInternalFormat::kText))
return base::UTF8ToUTF16(data.text());
if (ContainsFormat(data, ui::ClipboardInternalFormat::kHtml))
return base::UTF8ToUTF16(data.markup_data());
if (ContainsFormat(data, ui::ClipboardInternalFormat::kRtf))
return GetLocalizedString(IDS_CLIPBOARD_MENU_RTF_CONTENT);
if (ContainsFormat(data, ui::ClipboardInternalFormat::kBookmark))
return base::UTF8ToUTF16(data.bookmark_title());
if (ContainsFormat(data, ui::ClipboardInternalFormat::kWeb))
return GetLocalizedString(IDS_CLIPBOARD_MENU_WEB_SMART_PASTE);
if (ContainsFormat(data, ui::ClipboardInternalFormat::kCustom))
return GetLabelForCustomData(data);
NOTREACHED();
return base::string16();
}
} // namespace helper
} // namespace clipboard
} // namespace ash
// 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.
#ifndef ASH_CLIPBOARD_CLIPBOARD_HISTORY_HELPER_H_
#define ASH_CLIPBOARD_CLIPBOARD_HISTORY_HELPER_H_
#include "ash/ash_export.h"
#include "base/strings/string16.h"
namespace ui {
class ClipboardData;
} // namespace ui
namespace ash {
namespace clipboard {
namespace helper {
// Returns the label to display for the specified clipboard |data|.
ASH_EXPORT base::string16 GetLabel(const ui::ClipboardData& data);
} // namespace helper
} // namespace clipboard
} // namespace ash
#endif // ASH_CLIPBOARD_CLIPBOARD_HISTORY_HELPER_H_
// 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_helper.h"
#include <string>
#include <unordered_map>
#include "base/optional.h"
#include "base/pickle.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/icu_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/clipboard_data.h"
#include "ui/base/clipboard/clipboard_format_type.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/gfx/image/image_unittest_util.h"
namespace ash {
namespace clipboard {
namespace helper {
namespace {
// ClipboardDataBuilder --------------------------------------------------------
class ClipboardDataBuilder {
public:
ClipboardDataBuilder() = default;
ClipboardDataBuilder(const ClipboardDataBuilder&) = delete;
ClipboardDataBuilder& operator=(const ClipboardDataBuilder&) = delete;
~ClipboardDataBuilder() = default;
ui::ClipboardData Build() const {
ui::ClipboardData data;
if (text_.has_value())
data.set_text(text_.value());
if (markup_.has_value())
data.set_markup_data(markup_.value());
if (rtf_.has_value())
data.SetRTFData(rtf_.value());
if (bookmark_title_.has_value())
data.set_bookmark_title(bookmark_title_.value());
if (bitmap_.has_value())
data.SetBitmapData(bitmap_.value());
if (custom_format_.has_value() && custom_data_.has_value())
data.SetCustomData(custom_format_.value(), custom_data_.value());
if (web_smart_paste_.has_value())
data.set_web_smart_paste(web_smart_paste_.value());
return data;
}
ClipboardDataBuilder& SetText(const std::string& text) {
text_ = text;
return *this;
}
ClipboardDataBuilder& ClearText() {
text_ = base::nullopt;
return *this;
}
ClipboardDataBuilder& SetMarkup(const std::string& markup) {
markup_ = markup;
return *this;
}
ClipboardDataBuilder& ClearMarkup() {
markup_ = base::nullopt;
return *this;
}
ClipboardDataBuilder& SetRtf(const std::string& rtf) {
rtf_ = rtf;
return *this;
}
ClipboardDataBuilder& ClearRtf() {
rtf_ = base::nullopt;
return *this;
}
ClipboardDataBuilder& SetBookmarkTitle(const std::string& bookmark_title) {
bookmark_title_ = bookmark_title;
return *this;
}
ClipboardDataBuilder& ClearBookmarkTitle() {
bookmark_title_ = base::nullopt;
return *this;
}
ClipboardDataBuilder& SetBitmap(const SkBitmap& bitmap) {
bitmap_ = bitmap;
return *this;
}
ClipboardDataBuilder& ClearBitmap() {
bitmap_ = base::nullopt;
return *this;
}
ClipboardDataBuilder& SetCustomData(const std::string& custom_format,
const std::string& custom_data) {
custom_format_ = custom_format;
custom_data_ = custom_data;
return *this;
}
ClipboardDataBuilder& ClearCustomData() {
custom_format_ = base::nullopt;
custom_data_ = base::nullopt;
return *this;
}
ClipboardDataBuilder& SetFileSystemData(
std::initializer_list<std::string>&& source_list) {
constexpr char kFileSystemSourcesType[] = "fs/sources";
base::Pickle custom_data;
ui::WriteCustomDataToPickle(
std::unordered_map<base::string16, base::string16>(
{{base::UTF8ToUTF16(kFileSystemSourcesType),
base::UTF8ToUTF16(base::JoinString(source_list, "\n"))}}),
&custom_data);
return SetCustomData(
ui::ClipboardFormatType::GetWebCustomDataType().GetName(),
std::string(static_cast<const char*>(custom_data.data()),
custom_data.size()));
}
ClipboardDataBuilder& SetWebSmartPaste(bool web_smart_paste) {
web_smart_paste_ = web_smart_paste;
return *this;
}
ClipboardDataBuilder& ClearWebSmartPaste() {
web_smart_paste_ = base::nullopt;
return *this;
}
private:
base::Optional<std::string> text_;
base::Optional<std::string> markup_;
base::Optional<std::string> rtf_;
base::Optional<std::string> bookmark_title_;
base::Optional<SkBitmap> bitmap_;
base::Optional<std::string> custom_format_;
base::Optional<std::string> custom_data_;
base::Optional<bool> web_smart_paste_;
};
} // namespace
// Tests -----------------------------------------------------------------------
using ClipboardHistoryHelperTest = testing::Test;
TEST_F(ClipboardHistoryHelperTest, GetLabel) {
base::test::ScopedRestoreICUDefaultLocale locale("en_US");
// Populate a builder with all the data formats that we expect to handle.
ClipboardDataBuilder builder;
builder.SetText("Text")
.SetMarkup("Markup")
.SetRtf("Rtf")
.SetBookmarkTitle("Bookmark Title")
.SetBitmap(gfx::test::CreateBitmap(10, 10))
.SetCustomData("Custom Format", "Custom Data")
.SetWebSmartPaste(true);
// Bitmap data always take precedence.
EXPECT_EQ(GetLabel(builder.Build()), base::UTF8ToUTF16("Image"));
builder.ClearBitmap();
// In the absence of bitmap data, text data takes precedence.
EXPECT_EQ(GetLabel(builder.Build()), base::UTF8ToUTF16("Text"));
builder.ClearText();
// In the absence of text data, HTML data takes precedence.
EXPECT_EQ(GetLabel(builder.Build()), base::UTF8ToUTF16("Markup"));
builder.ClearMarkup();
// In the absence of HTML data, RTF data takes precedence.
EXPECT_EQ(GetLabel(builder.Build()), base::UTF8ToUTF16("RTF Content"));
builder.ClearRtf();
// In the absence of RTF data, bookmark data takes precedence.
EXPECT_EQ(GetLabel(builder.Build()), base::UTF8ToUTF16("Bookmark Title"));
builder.ClearBookmarkTitle();
// In the absence of bookmark data, web smart paste data takes precedence.
EXPECT_EQ(GetLabel(builder.Build()),
base::UTF8ToUTF16("Web Smart Paste Content"));
builder.ClearWebSmartPaste();
// In the absence of web smart paste data, custom data takes precedence.
EXPECT_EQ(GetLabel(builder.Build()), base::UTF8ToUTF16("<Custom Data>"));
builder.SetFileSystemData(
{"/path/to/My%20File.txt", "/path/to/My%20Other%20File.txt"});
// We specially treat custom file system data to show a list of file names.
EXPECT_EQ(GetLabel(builder.Build()),
base::UTF8ToUTF16("My File.txt, My Other File.txt"));
}
} // namespace helper
} // namespace clipboard
} // namespace ash
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment