Skip to content

Commit

Permalink
Add file support to clipboard
Browse files Browse the repository at this point in the history
Adds ReadFilenames and WriteFilenames functions to clipboard. Filenames
corresponds generally to mime type text/uri-list, however on mac and
windows, there are specific types.

This functionality matches with the OSExchangeData Filenames support.

Bug: 1175483
Change-Id: Ib9f17db7f09c3f901db42d2b7e5a43c186a9cb2c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2651770
Reviewed-by: Darwin Huang <huangdarwin@chromium.org>
Reviewed-by: David Vallet <dvallet@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Alex Newcomer <newcomer@chromium.org>
Commit-Queue: Joel Hockey <joelhockey@chromium.org>
Cr-Commit-Position: refs/heads/master@{#855085}
  • Loading branch information
Joel Hockey authored and Chromium LUCI CQ committed Feb 18, 2021
1 parent 07c4661 commit f4394c6
Show file tree
Hide file tree
Showing 40 changed files with 562 additions and 74 deletions.
3 changes: 3 additions & 0 deletions ash/clipboard/clipboard_history_resource_manager.cc
Expand Up @@ -169,6 +169,9 @@ base::string16 ClipboardHistoryResourceManager::GetLabel(
case ui::ClipboardInternalFormat::kRtf:
RecordPlaceholderString(ClipboardHistoryPlaceholderStringType::kRtf);
return GetLocalizedString(IDS_CLIPBOARD_MENU_RTF_CONTENT);
case ui::ClipboardInternalFormat::kFilenames:
DCHECK(!data.filenames().empty());
return base::UTF8ToUTF16(data.filenames()[0].display_name.value());
case ui::ClipboardInternalFormat::kBookmark:
return base::UTF8ToUTF16(data.bookmark_title());
case ui::ClipboardInternalFormat::kWeb:
Expand Down
8 changes: 8 additions & 0 deletions ash/clipboard/clipboard_history_resource_manager_unittest.cc
Expand Up @@ -127,6 +127,8 @@ TEST_F(ClipboardHistoryResourceManagerTest, GetLabel) {
builder.SetText("Text")
.SetMarkup("HTML with no image or table tags")
.SetRtf("Rtf")
.SetFilenames({ui::FileInfo(base::FilePath("/dir/filename"),
base::FilePath("filename"))})
.SetBookmarkTitle("Bookmark Title")
.SetBitmap(gfx::test::CreateBitmap(10, 10))
.SetFileSystemData({"/path/to/File.txt", "/path/to/Other%20File.txt"})
Expand Down Expand Up @@ -165,6 +167,12 @@ TEST_F(ClipboardHistoryResourceManagerTest, GetLabel) {

builder.ClearRtf();

// In the absence of RTF data, Filenames data takes precedence.
EXPECT_EQ(resource_manager()->GetLabel(builder.Build()),
base::UTF8ToUTF16("filename"));

builder.ClearFilenames();

// In the absence of RTF data, bookmark data takes precedence.
EXPECT_EQ(resource_manager()->GetLabel(builder.Build()),
base::UTF8ToUTF16("Bookmark Title"));
Expand Down
11 changes: 8 additions & 3 deletions ash/clipboard/clipboard_history_util.cc
Expand Up @@ -24,9 +24,13 @@ constexpr char kFileSystemSourcesType[] = "fs/sources";

// The array of formats in order of decreasing priority.
constexpr ui::ClipboardInternalFormat kPrioritizedFormats[] = {
ui::ClipboardInternalFormat::kBitmap, ui::ClipboardInternalFormat::kHtml,
ui::ClipboardInternalFormat::kText, ui::ClipboardInternalFormat::kRtf,
ui::ClipboardInternalFormat::kBookmark, ui::ClipboardInternalFormat::kWeb,
ui::ClipboardInternalFormat::kBitmap,
ui::ClipboardInternalFormat::kHtml,
ui::ClipboardInternalFormat::kText,
ui::ClipboardInternalFormat::kRtf,
ui::ClipboardInternalFormat::kFilenames,
ui::ClipboardInternalFormat::kBookmark,
ui::ClipboardInternalFormat::kWeb,
ui::ClipboardInternalFormat::kCustom};

} // namespace
Expand Down Expand Up @@ -55,6 +59,7 @@ ClipboardHistoryDisplayFormat CalculateDisplayFormat(
case ui::ClipboardInternalFormat::kText:
case ui::ClipboardInternalFormat::kSvg:
case ui::ClipboardInternalFormat::kRtf:
case ui::ClipboardInternalFormat::kFilenames:
case ui::ClipboardInternalFormat::kBookmark:
case ui::ClipboardInternalFormat::kWeb:
return ClipboardHistoryDisplayFormat::kText;
Expand Down
18 changes: 18 additions & 0 deletions ash/clipboard/test_support/clipboard_history_item_builder.cc
Expand Up @@ -28,6 +28,8 @@ ClipboardHistoryItem ClipboardHistoryItemBuilder::Build() const {
data.set_markup_data(markup_.value());
if (rtf_.has_value())
data.SetRTFData(rtf_.value());
if (!filenames_.empty())
data.set_filenames(filenames_);
if (bookmark_title_.has_value())
data.set_bookmark_title(bookmark_title_.value());
if (bitmap_.has_value())
Expand Down Expand Up @@ -62,6 +64,9 @@ ClipboardHistoryItemBuilder& ClipboardHistoryItemBuilder::SetFormat(
return SetMarkup("Svg");
case ui::ClipboardInternalFormat::kRtf:
return SetRtf("Rtf");
case ui::ClipboardInternalFormat::kFilenames:
return SetFilenames({ui::FileInfo(base::FilePath("/dir/filename"),
base::FilePath("filename"))});
case ui::ClipboardInternalFormat::kBookmark:
return SetBookmarkTitle("Bookmark Title");
case ui::ClipboardInternalFormat::kBitmap:
Expand All @@ -85,6 +90,8 @@ ClipboardHistoryItemBuilder& ClipboardHistoryItemBuilder::ClearFormat(
return ClearSvg();
case ui::ClipboardInternalFormat::kRtf:
return ClearRtf();
case ui::ClipboardInternalFormat::kFilenames:
return ClearFilenames();
case ui::ClipboardInternalFormat::kBookmark:
return ClearBookmarkTitle();
case ui::ClipboardInternalFormat::kBitmap:
Expand Down Expand Up @@ -142,6 +149,17 @@ ClipboardHistoryItemBuilder& ClipboardHistoryItemBuilder::ClearRtf() {
return *this;
}

ClipboardHistoryItemBuilder& ClipboardHistoryItemBuilder::SetFilenames(
std::vector<ui::FileInfo> filenames) {
filenames_ = std::move(filenames);
return *this;
}

ClipboardHistoryItemBuilder& ClipboardHistoryItemBuilder::ClearFilenames() {
filenames_.clear();
return *this;
}

ClipboardHistoryItemBuilder& ClipboardHistoryItemBuilder::SetBookmarkTitle(
const std::string& bookmark_title) {
bookmark_title_ = bookmark_title;
Expand Down
7 changes: 7 additions & 0 deletions ash/clipboard/test_support/clipboard_history_item_builder.h
Expand Up @@ -10,6 +10,7 @@
#include "ash/ash_export.h"
#include "base/optional.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/file_info.h"

namespace ui {
enum class ClipboardInternalFormat;
Expand Down Expand Up @@ -56,6 +57,11 @@ class ASH_EXPORT ClipboardHistoryItemBuilder {
ClipboardHistoryItemBuilder& SetRtf(const std::string& rtf);
ClipboardHistoryItemBuilder& ClearRtf();

// Sets/clears `filenames_` data.
ClipboardHistoryItemBuilder& SetFilenames(
std::vector<ui::FileInfo> filenames);
ClipboardHistoryItemBuilder& ClearFilenames();

// Sets/clears `bookmark_title_` data.
ClipboardHistoryItemBuilder& SetBookmarkTitle(
const std::string& bookmark_title);
Expand Down Expand Up @@ -85,6 +91,7 @@ class ASH_EXPORT ClipboardHistoryItemBuilder {
base::Optional<std::string> markup_;
base::Optional<std::string> svg_;
base::Optional<std::string> rtf_;
std::vector<ui::FileInfo> filenames_;
base::Optional<std::string> bookmark_title_;
base::Optional<SkBitmap> bitmap_;
base::Optional<std::string> custom_format_;
Expand Down
12 changes: 12 additions & 0 deletions headless/lib/browser/headless_clipboard.cc
Expand Up @@ -164,6 +164,14 @@ void HeadlessClipboard::ReadCustomData(ui::ClipboardBuffer clipboard_buffer,
const ui::DataTransferEndpoint* data_dst,
base::string16* result) const {}

// |data_dst| is not used. It's only passed to be consistent with other
// platforms.
void HeadlessClipboard::ReadFilenames(ui::ClipboardBuffer buffer,
const ui::DataTransferEndpoint* data_dst,
std::vector<ui::FileInfo>* result) const {
*result = GetStore(buffer).filenames;
}

// |data_dst| is not used. It's only passed to be consistent with other
// platforms.
void HeadlessClipboard::ReadBookmark(const ui::DataTransferEndpoint* data_dst,
Expand Down Expand Up @@ -249,6 +257,10 @@ void HeadlessClipboard::WriteRTF(const char* rtf_data, size_t data_len) {
std::string(rtf_data, data_len);
}

void HeadlessClipboard::WriteFilenames(std::vector<ui::FileInfo> filenames) {
GetDefaultStore().filenames = std::move(filenames);
}

void HeadlessClipboard::WriteBookmark(const char* title_data,
size_t title_len,
const char* url_data,
Expand Down
5 changes: 5 additions & 0 deletions headless/lib/browser/headless_clipboard.h
Expand Up @@ -64,6 +64,9 @@ class HeadlessClipboard : public ui::Clipboard {
const base::string16& type,
const ui::DataTransferEndpoint* data_dst,
base::string16* result) const override;
void ReadFilenames(ui::ClipboardBuffer buffer,
const ui::DataTransferEndpoint* data_dst,
std::vector<ui::FileInfo>* result) const override;
void ReadBookmark(const ui::DataTransferEndpoint* data_dst,
base::string16* title,
std::string* url) const override;
Expand All @@ -88,6 +91,7 @@ class HeadlessClipboard : public ui::Clipboard {
size_t url_len) override;
void WriteSvg(const char* markup_data, size_t markup_len) override;
void WriteRTF(const char* rtf_data, size_t data_len) override;
void WriteFilenames(std::vector<ui::FileInfo> filenames) override;
void WriteBookmark(const char* title_data,
size_t title_len,
const char* url_data,
Expand All @@ -108,6 +112,7 @@ class HeadlessClipboard : public ui::Clipboard {
std::string url_title;
std::string html_src_url;
SkBitmap image;
std::vector<ui::FileInfo> filenames;
};

// The non-const versions increment the sequence number as a side effect.
Expand Down
2 changes: 2 additions & 0 deletions tools/metrics/histograms/enums.xml
Expand Up @@ -11664,6 +11664,7 @@ histogram as enum -->
<int value="6" label="CustomData"/>
<int value="7" label="WebSmartPaste"/>
<int value="8" label="Svg"/>
<int value="9" label="Filenames"/>
</enum>

<enum name="ClipboardFormatWrite">
Expand All @@ -11676,6 +11677,7 @@ histogram as enum -->
<int value="6" label="CustomData"/>
<int value="7" label="WebSmartPaste"/>
<int value="8" label="Svg"/>
<int value="9" label="Filenames"/>
</enum>

<enum name="ClipboardHistoryDisplayFormat">
Expand Down
2 changes: 2 additions & 0 deletions ui/base/clipboard/BUILD.gn
Expand Up @@ -125,6 +125,7 @@ component("clipboard") {

public_deps = [
":clipboard_types",
":file_info",
"//ui/base/data_transfer_policy",
]

Expand Down Expand Up @@ -254,6 +255,7 @@ source_set("clipboard_test") {
"//base/test:test_support",
"//testing/gmock",
"//testing/gtest",
"//ui/base:features",
"//ui/events/platform",
"//ui/gfx:test_support",
"//url:url",
Expand Down
6 changes: 6 additions & 0 deletions ui/base/clipboard/clipboard.cc
Expand Up @@ -183,6 +183,12 @@ void Clipboard::DispatchPortableRepresentation(PortableFormat format,
break;
}

case PortableFormat::kFilenames: {
std::string uri_list(&(params[0].front()), params[0].size());
WriteFilenames(ui::URIListToFileInfos(uri_list));
break;
}

case PortableFormat::kData:
WriteData(ClipboardFormatType::Deserialize(
std::string(&(params[0].front()), params[0].size())),
Expand Down
34 changes: 22 additions & 12 deletions ui/base/clipboard/clipboard.h
Expand Up @@ -28,6 +28,7 @@
#include "mojo/public/cpp/base/big_buffer.h"
#include "ui/base/clipboard/clipboard_buffer.h"
#include "ui/base/clipboard/clipboard_format_type.h"
#include "ui/base/clipboard/file_info.h"
#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"

class SkBitmap;
Expand Down Expand Up @@ -175,6 +176,11 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard
const DataTransferEndpoint* data_dst,
base::string16* result) const = 0;

// Reads filenames from the clipboard, if available.
virtual void ReadFilenames(ClipboardBuffer buffer,
const DataTransferEndpoint* data_dst,
std::vector<ui::FileInfo>* result) const = 0;

// Reads a bookmark from the clipboard, if available.
// |title| or |url| may be null.
virtual void ReadBookmark(const DataTransferEndpoint* data_dst,
Expand Down Expand Up @@ -217,6 +223,7 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard
kWebkit,
kData, // Arbitrary block of bytes.
kSvg,
kFilenames,
};

// TODO (https://crbug.com/994928): Rename ObjectMap-related types.
Expand All @@ -227,18 +234,19 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard
//
// Key Arguments Type
// -------------------------------------
// kBitmap bitmap A pointer to a SkBitmap. The caller must ensure
// the SkBitmap remains live for the duration of
// the WritePortableRepresentations call.
// kHtml html char array
// url* char array
// kRtf data byte array
// kBookmark html char array
// url char array
// kText text char array
// kWebkit none empty vector
// kData format char array
// data byte array
// kBitmap bitmap A pointer to a SkBitmap. The caller must ensure
// the SkBitmap remains live for the duration of
// the WritePortableRepresentations call.
// kHtml html char array
// url* char array
// kRtf data byte array
// kFilenames text/uri-list char array
// kBookmark html char array
// url char array
// kText text char array
// kWebkit none empty vector
// kData format char array
// data byte array
using ObjectMapParam = std::vector<char>;
using ObjectMapParams = std::vector<ObjectMapParam>;
using ObjectMap = base::flat_map<PortableFormat, ObjectMapParams>;
Expand Down Expand Up @@ -297,6 +305,8 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard

virtual void WriteRTF(const char* rtf_data, size_t data_len) = 0;

virtual void WriteFilenames(std::vector<ui::FileInfo> filenames) = 0;

virtual void WriteBookmark(const char* title_data,
size_t title_len,
const char* url_data,
Expand Down
13 changes: 13 additions & 0 deletions ui/base/clipboard/clipboard_android.cc
Expand Up @@ -531,6 +531,15 @@ void ClipboardAndroid::ReadCustomData(ClipboardBuffer buffer,
NOTIMPLEMENTED();
}

// |data_dst| is not used. It's only passed to be consistent with other
// platforms.
void ClipboardAndroid::ReadFilenames(ClipboardBuffer buffer,
const DataTransferEndpoint* data_dst,
std::vector<ui::FileInfo>* result) const {
DCHECK(CalledOnValidThread());
NOTIMPLEMENTED();
}

// |data_dst| is not used. It's only passed to be consistent with other
// platforms.
void ClipboardAndroid::ReadBookmark(const DataTransferEndpoint* data_dst,
Expand Down Expand Up @@ -614,6 +623,10 @@ void ClipboardAndroid::WriteRTF(const char* rtf_data, size_t data_len) {
NOTIMPLEMENTED();
}

void ClipboardAndroid::WriteFilenames(std::vector<ui::FileInfo> filenames) {
NOTIMPLEMENTED();
}

// According to other platforms implementations, this really writes the
// URL spec.
void ClipboardAndroid::WriteBookmark(const char* title_data,
Expand Down
4 changes: 4 additions & 0 deletions ui/base/clipboard/clipboard_android.h
Expand Up @@ -95,6 +95,9 @@ class ClipboardAndroid : public Clipboard {
const base::string16& type,
const DataTransferEndpoint* data_dst,
base::string16* result) const override;
void ReadFilenames(ClipboardBuffer buffer,
const DataTransferEndpoint* data_dst,
std::vector<ui::FileInfo>* result) const override;
void ReadBookmark(const DataTransferEndpoint* data_dst,
base::string16* title,

Expand All @@ -119,6 +122,7 @@ class ClipboardAndroid : public Clipboard {
size_t url_len) override;
void WriteSvg(const char* markup_data, size_t markup_len) override;
void WriteRTF(const char* rtf_data, size_t data_len) override;
void WriteFilenames(std::vector<ui::FileInfo> filenames) override;
void WriteBookmark(const char* title_data,
size_t title_len,
const char* url_data,
Expand Down
3 changes: 2 additions & 1 deletion ui/base/clipboard/clipboard_data.cc
Expand Up @@ -29,6 +29,7 @@ ClipboardData::ClipboardData(const ClipboardData& other) {
custom_data_data_ = other.custom_data_data_;
web_smart_paste_ = other.web_smart_paste_;
svg_data_ = other.svg_data_;
filenames_ = other.filenames_;
src_ = other.src_ ? std::make_unique<DataTransferEndpoint>(*other.src_.get())
: nullptr;
}
Expand All @@ -46,7 +47,7 @@ bool ClipboardData::operator==(const ClipboardData& that) const {
custom_data_format_ == that.custom_data_format() &&
custom_data_data_ == that.custom_data_data() &&
web_smart_paste_ == that.web_smart_paste() &&
svg_data_ == that.svg_data() &&
svg_data_ == that.svg_data() && filenames_ == that.filenames() &&
gfx::BitmapsAreEqual(bitmap_, that.bitmap()) &&
(src_.get() ? (that.source() && *src_.get() == *that.source())
: !that.source());
Expand Down
12 changes: 12 additions & 0 deletions ui/base/clipboard/clipboard_data.h
Expand Up @@ -10,6 +10,7 @@

#include "base/component_export.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/file_info.h"
#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"

class SkBitmap;
Expand All @@ -26,6 +27,7 @@ enum class ClipboardInternalFormat {
kBitmap = 1 << 5,
kCustom = 1 << 6,
kWeb = 1 << 7,
kFilenames = 1 << 8,
};

// ClipboardData contains data copied to the Clipboard for a variety of formats.
Expand Down Expand Up @@ -100,6 +102,13 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardData {
format_ |= static_cast<int>(ClipboardInternalFormat::kWeb);
}

const std::vector<ui::FileInfo>& filenames() const { return filenames_; }
void set_filenames(std::vector<ui::FileInfo> filenames) {
filenames_ = std::move(filenames);
if (!filenames_.empty())
format_ |= static_cast<int>(ClipboardInternalFormat::kFilenames);
}

DataTransferEndpoint* source() const { return src_.get(); }

void set_source(std::unique_ptr<DataTransferEndpoint> src) {
Expand Down Expand Up @@ -134,6 +143,9 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardData {
// Svg data.
std::string svg_data_;

// text/uri-list filenames data.
std::vector<ui::FileInfo> filenames_;

int format_;

// The source of the data.
Expand Down

0 comments on commit f4394c6

Please sign in to comment.