[Clipboard API] Clipboard Custom Formats implementation Part 5.
In this patch a new web custom format map is introduced while reading & writing custom format from/to the clipboard. This format is in JSON type and contains the mapping of custom MIME type to web custom clipboard format. It also adds a restriction as to how many web custom formats can be added via pickling async clipboard APIs. Currently the hard limit is 100 on all platforms. This was a security feedback in https://chromium-review.googlesource.com/c/chromium/src/+/3002583/comments/75f23531_61662b80 Also this format map is passed via `WritePortableAndPlatformRepresentations` method to write the custom format mapping to the clipboard. i2p: https://groups.google.com/a/chromium.org/g/blink-dev/c/Lo7WBM_v_LY/m/LncCKkXeAwAJ Bug: 106449, 1217643 Change-Id: I252f9eb5e5c6da7a60baf84e51743eea3f196161 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3039323 Commit-Queue: Anupam Snigdha <snianu@microsoft.com> Reviewed-by: Daniel Cheng <dcheng@chromium.org> Reviewed-by: Marijn Kruisselbrink <mek@chromium.org> Reviewed-by: Eric Seckler <eseckler@chromium.org> Cr-Commit-Position: refs/heads/master@{#908188}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
f43582955e
commit
22a48f046a
headless/lib/browser
ui/base/clipboard
@ -76,6 +76,10 @@ HeadlessClipboard::ReadAvailablePlatformSpecificFormatNames(
|
||||
const auto& data = GetStore(buffer).data;
|
||||
std::vector<std::u16string> types;
|
||||
types.reserve(data.size());
|
||||
std::map<std::string, std::string> custom_format_names =
|
||||
ExtractCustomPlatformNames(buffer, data_dst);
|
||||
for (const auto& item : custom_format_names)
|
||||
types.push_back(base::UTF8ToUTF16(item.first));
|
||||
for (const auto& it : data) {
|
||||
std::u16string type = base::UTF8ToUTF16(it.first.GetName());
|
||||
types.push_back(type);
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/json/json_reader.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/notreached.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
@ -137,6 +138,33 @@ base::Time Clipboard::GetLastModifiedTime() const {
|
||||
|
||||
void Clipboard::ClearLastModifiedTime() {}
|
||||
|
||||
std::map<std::string, std::string> Clipboard::ExtractCustomPlatformNames(
|
||||
ClipboardBuffer buffer,
|
||||
const DataTransferEndpoint* data_dst) const {
|
||||
// Read the JSON metadata payload.
|
||||
std::map<std::string, std::string> custom_format_names;
|
||||
if (IsFormatAvailable(ui::ClipboardFormatType::WebCustomFormatMap(), buffer,
|
||||
data_dst)) {
|
||||
std::string custom_format_json;
|
||||
// Read the custom format map.
|
||||
ReadData(ui::ClipboardFormatType::WebCustomFormatMap(), data_dst,
|
||||
&custom_format_json);
|
||||
if (!custom_format_json.empty()) {
|
||||
absl::optional<base::Value> json_val =
|
||||
base::JSONReader::Read(custom_format_json);
|
||||
if (json_val.has_value()) {
|
||||
for (const auto it : json_val->DictItems()) {
|
||||
std::string custom_format_name;
|
||||
if (it.second.GetAsString(&custom_format_name)) {
|
||||
custom_format_names.emplace(it.first, custom_format_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return custom_format_names;
|
||||
}
|
||||
|
||||
Clipboard::Clipboard() = default;
|
||||
Clipboard::~Clipboard() = default;
|
||||
|
||||
@ -203,6 +231,11 @@ void Clipboard::DispatchPortableRepresentation(PortableFormat format,
|
||||
&(params[1].front()), params[1].size());
|
||||
break;
|
||||
|
||||
case PortableFormat::kWebCustomFormatMap:
|
||||
WriteData(ClipboardFormatType::WebCustomFormatMap(),
|
||||
&(params[0].front()), params[0].size());
|
||||
break;
|
||||
|
||||
default:
|
||||
NOTREACHED();
|
||||
}
|
||||
@ -211,7 +244,7 @@ void Clipboard::DispatchPortableRepresentation(PortableFormat format,
|
||||
void Clipboard::DispatchPlatformRepresentations(
|
||||
std::vector<Clipboard::PlatformRepresentation> platform_representations) {
|
||||
for (const auto& representation : platform_representations) {
|
||||
WriteData(ClipboardFormatType::GetCustomPlatformType(representation.format),
|
||||
WriteData(ClipboardFormatType::CustomPlatformType(representation.format),
|
||||
reinterpret_cast<const char*>(representation.data.data()),
|
||||
representation.data.size());
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -265,6 +266,15 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard
|
||||
// Resets the clipboard last modified time to Time::Time().
|
||||
virtual void ClearLastModifiedTime();
|
||||
|
||||
// Reads the web custom format map (which is in JSON format) from the
|
||||
// clipboard if it's available. Parses the JSON string that has the mapping of
|
||||
// MIME type to custom format name and fetches the list of custom MIME types.
|
||||
// e.g. on Windows, the mapping is represented as "text/html":"Web Custom
|
||||
// Format(0-99)".
|
||||
std::map<std::string, std::string> ExtractCustomPlatformNames(
|
||||
ClipboardBuffer buffer,
|
||||
const DataTransferEndpoint* data_dst) const;
|
||||
|
||||
protected:
|
||||
// PortableFormat designates the type of data to be stored in the clipboard.
|
||||
// This designation is shared across all OSes. The system-specific designation
|
||||
@ -291,6 +301,7 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard
|
||||
kData, // Arbitrary block of bytes.
|
||||
kSvg,
|
||||
kFilenames,
|
||||
kWebCustomFormatMap,
|
||||
};
|
||||
|
||||
// TODO (https://crbug.com/994928): Rename ObjectMap-related types.
|
||||
@ -314,6 +325,7 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard
|
||||
// kWebkit none empty vector
|
||||
// kData format char array
|
||||
// data byte array
|
||||
// kWebCustomFormatMap char array
|
||||
using ObjectMapParam = std::vector<char>;
|
||||
using ObjectMapParams = std::vector<ObjectMapParam>;
|
||||
using ObjectMap = base::flat_map<PortableFormat, ObjectMapParams>;
|
||||
|
@ -82,19 +82,19 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) ClipboardFormatType {
|
||||
static const ClipboardFormatType& MozUrlType();
|
||||
#endif
|
||||
|
||||
// Gets the ClipboardFormatType corresponding to an arbitrary format string,
|
||||
// registering it with the system if needed. Due to Windows/Linux
|
||||
// limitations, please place limits on the amount of GetType calls with unique
|
||||
// |format_string| arguments, when ingesting |format_string| from
|
||||
// untrusted sources, such as renderer processes. In Windows, a failure will
|
||||
// return an invalid format with Deserialize()'ed value of "0".
|
||||
// The custom format name is transformed to the appropriate custom platform
|
||||
// type name.
|
||||
static ClipboardFormatType GetCustomPlatformType(
|
||||
// For custom formats we hardcode the web custom format prefix and the index.
|
||||
// Due to Windows/Linux limitations, please place limits on the amount of
|
||||
// `WebCustomFormatName` calls with unique `index` argument.
|
||||
static std::string WebCustomFormatName(int index);
|
||||
// Gets the ClipboardFormatType corresponding to a format string,
|
||||
// registering it with the system if needed.
|
||||
static ClipboardFormatType CustomPlatformType(
|
||||
const std::string& format_string);
|
||||
// Returns a custom MIME type from custom format name.
|
||||
// e.g. On Windows, "Web Text HTML" is returned as "text/html".
|
||||
std::string GetCustomPlatformName() const;
|
||||
// Returns the web custom format map that has the mapping of MIME types to
|
||||
// custom format names.
|
||||
static const ClipboardFormatType& WebCustomFormatMap();
|
||||
// Returns the web custom format map name.
|
||||
static std::string WebCustomFormatMapName();
|
||||
|
||||
// ClipboardFormatType can be used in a set on some platforms.
|
||||
bool operator<(const ClipboardFormatType& other) const;
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
#include "ui/base/clipboard/clipboard_format_type.h"
|
||||
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "ui/base/clipboard/clipboard_constants.h"
|
||||
|
||||
namespace ui {
|
||||
@ -38,15 +41,29 @@ bool ClipboardFormatType::operator==(const ClipboardFormatType& other) const {
|
||||
return data_ == other.data_;
|
||||
}
|
||||
|
||||
// TODO(crbug.com/106449): Support custom formats.
|
||||
ClipboardFormatType ClipboardFormatType::GetCustomPlatformType(
|
||||
// static
|
||||
std::string ClipboardFormatType::WebCustomFormatName(int index) {
|
||||
return base::StrCat({"application/web;type=\"custom/format",
|
||||
base::NumberToString(index), "\""});
|
||||
}
|
||||
|
||||
// static
|
||||
std::string ClipboardFormatType::WebCustomFormatMapName() {
|
||||
return "application/web;type=\"custom/formatmap\"";
|
||||
}
|
||||
|
||||
// static
|
||||
ClipboardFormatType ClipboardFormatType::CustomPlatformType(
|
||||
const std::string& format_string) {
|
||||
DCHECK(base::IsStringASCII(format_string));
|
||||
return ClipboardFormatType::Deserialize(format_string);
|
||||
}
|
||||
|
||||
// TODO(crbug.com/106449): Support custom formats.
|
||||
std::string ClipboardFormatType::GetCustomPlatformName() const {
|
||||
return Serialize();
|
||||
// static
|
||||
const ClipboardFormatType& ClipboardFormatType::WebCustomFormatMap() {
|
||||
static base::NoDestructor<ClipboardFormatType> type(
|
||||
ClipboardFormatType::WebCustomFormatMapName());
|
||||
return *type;
|
||||
}
|
||||
|
||||
// Various predefined ClipboardFormatTypes.
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
#include "ui/base/clipboard/clipboard_format_type.h"
|
||||
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "ui/base/clipboard/clipboard_constants.h"
|
||||
|
||||
namespace ui {
|
||||
@ -41,15 +44,29 @@ bool ClipboardFormatType::operator==(const ClipboardFormatType& other) const {
|
||||
return data_ == other.data_;
|
||||
}
|
||||
|
||||
// TODO(crbug.com/106449): Support custom formats.
|
||||
ClipboardFormatType ClipboardFormatType::GetCustomPlatformType(
|
||||
// static
|
||||
ClipboardFormatType ClipboardFormatType::CustomPlatformType(
|
||||
const std::string& format_string) {
|
||||
DCHECK(base::IsStringASCII(format_string));
|
||||
return ClipboardFormatType::Deserialize(format_string);
|
||||
}
|
||||
|
||||
// TODO(crbug.com/106449): Support custom formats.
|
||||
std::string ClipboardFormatType::GetCustomPlatformName() const {
|
||||
return Serialize();
|
||||
// static
|
||||
std::string ClipboardFormatType::WebCustomFormatName(int index) {
|
||||
return base::StrCat({"application/web;type=\"custom/format",
|
||||
base::NumberToString(index), "\""});
|
||||
}
|
||||
|
||||
// static
|
||||
std::string ClipboardFormatType::WebCustomFormatMapName() {
|
||||
return "application/web;type=\"custom/formatmap\"";
|
||||
}
|
||||
|
||||
// static
|
||||
const ClipboardFormatType& ClipboardFormatType::WebCustomFormatMap() {
|
||||
static base::NoDestructor<ClipboardFormatType> type(
|
||||
ClipboardFormatType::WebCustomFormatMapName());
|
||||
return *type;
|
||||
}
|
||||
|
||||
// Various predefined ClipboardFormatTypes.
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "ui/base/clipboard/clipboard_constants.h"
|
||||
|
||||
@ -58,15 +61,27 @@ bool ClipboardFormatType::operator<(const ClipboardFormatType& other) const {
|
||||
}
|
||||
|
||||
// static
|
||||
// TODO(crbug.com/106449): Support custom formats.
|
||||
ClipboardFormatType ClipboardFormatType::GetCustomPlatformType(
|
||||
const std::string& format_string) {
|
||||
return ClipboardFormatType::Deserialize(format_string);
|
||||
std::string ClipboardFormatType::WebCustomFormatName(int index) {
|
||||
return base::StrCat({"com.web.custom.format", base::NumberToString(index)});
|
||||
}
|
||||
|
||||
// TODO(crbug.com/106449): Support custom formats.
|
||||
std::string ClipboardFormatType::GetCustomPlatformName() const {
|
||||
return Serialize();
|
||||
// static
|
||||
std::string ClipboardFormatType::WebCustomFormatMapName() {
|
||||
return "com.web.custom.format.map";
|
||||
}
|
||||
|
||||
// static
|
||||
const ClipboardFormatType& ClipboardFormatType::WebCustomFormatMap() {
|
||||
static base::NoDestructor<ClipboardFormatType> type(
|
||||
base::SysUTF8ToNSString(ClipboardFormatType::WebCustomFormatMapName()));
|
||||
return *type;
|
||||
}
|
||||
|
||||
// static
|
||||
ClipboardFormatType ClipboardFormatType::CustomPlatformType(
|
||||
const std::string& format_string) {
|
||||
DCHECK(base::IsStringASCII(format_string));
|
||||
return ClipboardFormatType::Deserialize(format_string);
|
||||
}
|
||||
|
||||
// Various predefined ClipboardFormatTypes.
|
||||
|
@ -58,74 +58,32 @@ ClipboardFormatType ClipboardFormatType::Deserialize(
|
||||
}
|
||||
|
||||
// static
|
||||
ClipboardFormatType ClipboardFormatType::GetCustomPlatformType(
|
||||
const std::string& format_string) {
|
||||
// For unsanitized custom formats, we add `Web ` prefix and capitalize the
|
||||
// first letter of each word in the format. e.g. `text/custom` format would be
|
||||
// converted to `Web Text Custom`. Similarly for `text/html` or any other
|
||||
// standard formats, the pickled version would be prefixed with `Web ` and
|
||||
// first letter capitalized. e.g. text/html would be converted to `Web Text
|
||||
// Html`.
|
||||
// For security reasons we also check for valid ascii codepoints.
|
||||
constexpr int kMinNameSize = 3; // Formats need to have at least 3 chars.
|
||||
if (!base::IsStringASCII(format_string) ||
|
||||
format_string.size() < kMinNameSize) {
|
||||
return ClipboardFormatType();
|
||||
}
|
||||
|
||||
size_t index = format_string.find('/');
|
||||
if (index == std::string::npos || index == 0 ||
|
||||
index == format_string.size() - 1)
|
||||
return ClipboardFormatType();
|
||||
base::StringPiece first_part =
|
||||
base::StringPiece(format_string).substr(0, index);
|
||||
base::StringPiece second_part =
|
||||
base::StringPiece(format_string).substr(index + 1);
|
||||
std::string web_custom_format_string = base::StrCat(
|
||||
{"Web ", base::ToUpperASCII(first_part.substr(0, 1)),
|
||||
first_part.substr(1), " ", base::ToUpperASCII(second_part.substr(0, 1)),
|
||||
second_part.substr(1)});
|
||||
return ClipboardFormatType(::RegisterClipboardFormat(
|
||||
base::ASCIIToWide(web_custom_format_string).c_str()));
|
||||
std::string ClipboardFormatType::WebCustomFormatName(int index) {
|
||||
return base::StrCat({"Web Custom Format", base::NumberToString(index)});
|
||||
}
|
||||
|
||||
// static
|
||||
std::string ClipboardFormatType::GetCustomPlatformName() const {
|
||||
constexpr size_t kMaxFormatSize = 1024;
|
||||
static base::NoDestructor<std::vector<wchar_t>> name_buffer(kMaxFormatSize);
|
||||
int name_size = GetClipboardFormatName(data_.cfFormat, name_buffer->data(),
|
||||
kMaxFormatSize);
|
||||
// Custom formats should have at least 7 characters. e.g. "Web x y"
|
||||
constexpr int kMinNameSize = 7;
|
||||
if (!name_size || name_size < kMinNameSize) {
|
||||
// Input format doesn't exist or is predefined.
|
||||
return std::string();
|
||||
}
|
||||
std::string ClipboardFormatType::WebCustomFormatMapName() {
|
||||
return "Web Custom Format Map";
|
||||
}
|
||||
|
||||
std::string format_name_in_clipboard =
|
||||
base::WideToASCII(std::wstring(name_buffer->data(), name_size));
|
||||
// static
|
||||
ClipboardFormatType ClipboardFormatType::CustomPlatformType(
|
||||
const std::string& format_string) {
|
||||
// Once these formats are registered, `RegisterClipboardFormat` just returns
|
||||
// the `cfFormat` associated with it and doesn't register a new format.
|
||||
DCHECK(base::IsStringASCII(format_string));
|
||||
return ClipboardFormatType(
|
||||
::RegisterClipboardFormat(base::ASCIIToWide(format_string).c_str()));
|
||||
}
|
||||
|
||||
// For security reasons we also check for valid ascii codepoints.
|
||||
if (!base::IsStringASCII(format_name_in_clipboard))
|
||||
return std::string();
|
||||
// For unsanitized custom formats (prefixed with Web) we extract the strings
|
||||
// and convert it into standard representation. e.g. `Web Text Custom` format
|
||||
// would be converted to `text/custom`.
|
||||
if (format_name_in_clipboard.substr(0, 4) == "Web ") {
|
||||
base::StringPiece format_name =
|
||||
base::StringPiece(format_name_in_clipboard).substr(4);
|
||||
size_t space_index = format_name.find(" ");
|
||||
|
||||
if (space_index == std::string::npos)
|
||||
return std::string();
|
||||
if (format_name.size() < (space_index + 2))
|
||||
return std::string();
|
||||
|
||||
return base::StrCat(
|
||||
{base::ToLowerASCII(format_name.substr(0, space_index)), "/",
|
||||
base::ToLowerASCII(format_name.substr(space_index + 1))});
|
||||
}
|
||||
return format_name_in_clipboard;
|
||||
// static
|
||||
const ClipboardFormatType& ClipboardFormatType::WebCustomFormatMap() {
|
||||
static base::NoDestructor<ClipboardFormatType> format(
|
||||
::RegisterClipboardFormat(
|
||||
base::ASCIIToWide(ClipboardFormatType::WebCustomFormatMapName())
|
||||
.c_str()));
|
||||
return *format;
|
||||
}
|
||||
|
||||
std::string ClipboardFormatType::GetName() const {
|
||||
|
@ -809,11 +809,11 @@ TYPED_TEST(ClipboardTest, MultiplePickleTest) {
|
||||
EXPECT_EQ(payload1, unpickled_string1);
|
||||
}
|
||||
|
||||
// TODO(crbug.com/106449): Implement custom formats on other platforms.
|
||||
#if defined(OS_WIN)
|
||||
TYPED_TEST(ClipboardTest, DataTest) {
|
||||
const std::string kFormatString = "chromium/x-test-format";
|
||||
const std::u16string kFormatString16 = u"chromium/x-test-format";
|
||||
const ClipboardFormatType kFormat =
|
||||
ClipboardFormatType::GetCustomPlatformType(kFormatString);
|
||||
const std::string payload = "test string";
|
||||
base::span<const uint8_t> payload_span(
|
||||
reinterpret_cast<const uint8_t*>(payload.data()), payload.size());
|
||||
@ -824,55 +824,28 @@ TYPED_TEST(ClipboardTest, DataTest) {
|
||||
mojo_base::BigBuffer(payload_span));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(this->clipboard().IsFormatAvailable(
|
||||
kFormat, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
|
||||
std::map<std::string, std::string> custom_format_names =
|
||||
this->clipboard().ExtractCustomPlatformNames(ClipboardBuffer::kCopyPaste,
|
||||
/* data_dst = */ nullptr);
|
||||
EXPECT_TRUE(custom_format_names.find(kFormatString) !=
|
||||
custom_format_names.end());
|
||||
std::string output;
|
||||
this->clipboard().ReadData(kFormat, /* data_dst = */ nullptr, &output);
|
||||
this->clipboard().ReadData(ClipboardFormatType::CustomPlatformType(
|
||||
custom_format_names[kFormatString]),
|
||||
/* data_dst = */ nullptr, &output);
|
||||
|
||||
EXPECT_EQ(payload, output);
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
TYPED_TEST(ClipboardTest, CustomFormatTypeAndNameTest) {
|
||||
const std::string kFormatString1 = "a/bc";
|
||||
const std::string kFormatString2 = "/abc";
|
||||
const std::string kFormatString3 = "abc/";
|
||||
const std::string kFormatString4 = "a/";
|
||||
const std::string kFormatString5 = "/a";
|
||||
const std::string kFormatString6 = "a/c";
|
||||
ClipboardFormatType format =
|
||||
ClipboardFormatType::GetCustomPlatformType(kFormatString1);
|
||||
EXPECT_EQ("a/bc", format.GetCustomPlatformName());
|
||||
format = ClipboardFormatType::GetCustomPlatformType(kFormatString2);
|
||||
EXPECT_EQ("", format.GetCustomPlatformName());
|
||||
format = ClipboardFormatType::GetCustomPlatformType(kFormatString3);
|
||||
EXPECT_EQ("", format.GetCustomPlatformName());
|
||||
format = ClipboardFormatType::GetCustomPlatformType(kFormatString4);
|
||||
EXPECT_EQ("", format.GetCustomPlatformName());
|
||||
format = ClipboardFormatType::GetCustomPlatformType(kFormatString5);
|
||||
EXPECT_EQ("", format.GetCustomPlatformName());
|
||||
format = ClipboardFormatType::GetCustomPlatformType(kFormatString6);
|
||||
EXPECT_EQ("a/c", format.GetCustomPlatformName());
|
||||
}
|
||||
#endif
|
||||
|
||||
// crbug.com/1224904: Flaky on Mac.
|
||||
#if (!defined(USE_AURA) || defined(OS_WIN) || defined(USE_OZONE) || \
|
||||
defined(USE_X11)) && \
|
||||
!BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OS_MAC)
|
||||
TYPED_TEST(ClipboardTest, MultipleDataTest) {
|
||||
const std::string kFormatString1 = "chromium/x-test-format1";
|
||||
const std::u16string kFormatString116 = u"chromium/x-test-format1";
|
||||
const ClipboardFormatType kFormat1 =
|
||||
ClipboardFormatType::GetCustomPlatformType(kFormatString1);
|
||||
const std::string payload1("test string1");
|
||||
base::span<const uint8_t> payload_span1(
|
||||
reinterpret_cast<const uint8_t*>(payload1.data()), payload1.size());
|
||||
|
||||
const std::string kFormatString2 = "chromium/x-test-format2";
|
||||
const std::u16string kFormatString216 = u"chromium/x-test-format2";
|
||||
const ClipboardFormatType kFormat2 =
|
||||
ClipboardFormatType::GetCustomPlatformType(kFormatString2);
|
||||
const std::string payload2("test string2");
|
||||
base::span<const uint8_t> payload_span2(
|
||||
reinterpret_cast<const uint8_t*>(payload2.data()), payload2.size());
|
||||
@ -890,36 +863,42 @@ TYPED_TEST(ClipboardTest, MultipleDataTest) {
|
||||
EXPECT_THAT(this->clipboard().ReadAvailablePlatformSpecificFormatNames(
|
||||
ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr),
|
||||
Contains(kFormatString116));
|
||||
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
|
||||
kFormat1, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
|
||||
std::string custom_format_json;
|
||||
this->clipboard().ReadData(ClipboardFormatType::WebCustomFormatMap(),
|
||||
/* data_dst = */ nullptr, &custom_format_json);
|
||||
std::map<std::string, std::string> custom_format_names =
|
||||
this->clipboard().ExtractCustomPlatformNames(ClipboardBuffer::kCopyPaste,
|
||||
/* data_dst = */ nullptr);
|
||||
EXPECT_TRUE(custom_format_names.find(kFormatString1) !=
|
||||
custom_format_names.end());
|
||||
std::string output1;
|
||||
this->clipboard().ReadData(kFormat1, /* data_dst = */ nullptr, &output1);
|
||||
this->clipboard().ReadData(ClipboardFormatType::CustomPlatformType(
|
||||
custom_format_names[kFormatString1]),
|
||||
/* data_dst = */ nullptr, &output1);
|
||||
EXPECT_EQ(payload1, output1);
|
||||
|
||||
// Check format 2.
|
||||
EXPECT_THAT(this->clipboard().ReadAvailablePlatformSpecificFormatNames(
|
||||
ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr),
|
||||
Contains(kFormatString216));
|
||||
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
|
||||
kFormat2, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
|
||||
EXPECT_TRUE(custom_format_names.find(kFormatString2) !=
|
||||
custom_format_names.end());
|
||||
std::string output2;
|
||||
this->clipboard().ReadData(kFormat2, /* data_dst = */ nullptr, &output2);
|
||||
this->clipboard().ReadData(ClipboardFormatType::CustomPlatformType(
|
||||
custom_format_names[kFormatString2]),
|
||||
/* data_dst = */ nullptr, &output2);
|
||||
EXPECT_EQ(payload2, output2);
|
||||
}
|
||||
|
||||
TYPED_TEST(ClipboardTest, DataAndPortableFormatTest) {
|
||||
const std::string kFormatString1 = "chromium/x-test-format1";
|
||||
const std::u16string kFormatString116 = u"chromium/x-test-format1";
|
||||
const ClipboardFormatType kFormat1 =
|
||||
ClipboardFormatType::GetCustomPlatformType(kFormatString1);
|
||||
const std::string payload1("test string1");
|
||||
base::span<const uint8_t> payload_span1(
|
||||
reinterpret_cast<const uint8_t*>(payload1.data()), payload1.size());
|
||||
|
||||
const std::string kFormatString2 = "text/plain";
|
||||
const std::u16string kFormatString216 = u"text/plain";
|
||||
const ClipboardFormatType kFormat2 =
|
||||
ClipboardFormatType::GetCustomPlatformType(kFormatString2);
|
||||
const std::string payload2("test string2");
|
||||
base::span<const uint8_t> payload_span2(
|
||||
reinterpret_cast<const uint8_t*>(payload2.data()), payload2.size());
|
||||
@ -937,20 +916,30 @@ TYPED_TEST(ClipboardTest, DataAndPortableFormatTest) {
|
||||
EXPECT_THAT(this->clipboard().ReadAvailablePlatformSpecificFormatNames(
|
||||
ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr),
|
||||
Contains(kFormatString116));
|
||||
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
|
||||
kFormat1, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
|
||||
std::string custom_format_json;
|
||||
this->clipboard().ReadData(ClipboardFormatType::WebCustomFormatMap(),
|
||||
/* data_dst = */ nullptr, &custom_format_json);
|
||||
std::map<std::string, std::string> custom_format_names =
|
||||
this->clipboard().ExtractCustomPlatformNames(ClipboardBuffer::kCopyPaste,
|
||||
/* data_dst = */ nullptr);
|
||||
EXPECT_TRUE(custom_format_names.find(kFormatString1) !=
|
||||
custom_format_names.end());
|
||||
std::string output1;
|
||||
this->clipboard().ReadData(kFormat1, /* data_dst = */ nullptr, &output1);
|
||||
this->clipboard().ReadData(ClipboardFormatType::CustomPlatformType(
|
||||
custom_format_names[kFormatString1]),
|
||||
/* data_dst = */ nullptr, &output1);
|
||||
EXPECT_EQ(payload1, output1);
|
||||
|
||||
// Check format 2.
|
||||
EXPECT_THAT(this->clipboard().ReadAvailablePlatformSpecificFormatNames(
|
||||
ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr),
|
||||
Contains(kFormatString216));
|
||||
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
|
||||
kFormat2, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
|
||||
EXPECT_TRUE(custom_format_names.find(kFormatString2) !=
|
||||
custom_format_names.end());
|
||||
std::string output2;
|
||||
this->clipboard().ReadData(kFormat2, /* data_dst = */ nullptr, &output2);
|
||||
this->clipboard().ReadData(ClipboardFormatType::CustomPlatformType(
|
||||
custom_format_names[kFormatString2]),
|
||||
/* data_dst = */ nullptr, &output2);
|
||||
EXPECT_EQ(payload2, output2);
|
||||
}
|
||||
#endif
|
||||
@ -1050,40 +1039,20 @@ TYPED_TEST(ClipboardTest, PlatformSpecificDataTest) {
|
||||
mojo_base::BigBuffer(text_span));
|
||||
}
|
||||
|
||||
const std::vector<std::u16string> raw_types =
|
||||
this->clipboard().ReadAvailablePlatformSpecificFormatNames(
|
||||
ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr);
|
||||
std::string custom_format_json;
|
||||
this->clipboard().ReadData(ClipboardFormatType::WebCustomFormatMap(),
|
||||
/* data_dst = */ nullptr, &custom_format_json);
|
||||
std::map<std::string, std::string> custom_format_names =
|
||||
this->clipboard().ExtractCustomPlatformNames(ClipboardBuffer::kCopyPaste,
|
||||
/* data_dst = */ nullptr);
|
||||
|
||||
EXPECT_THAT(raw_types, Contains(ASCIIToUTF16(kFormatString)));
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Custom format is only available on Windows.
|
||||
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
|
||||
ClipboardFormatType::GetCustomPlatformType(kFormatString),
|
||||
ClipboardBuffer::kCopyPaste,
|
||||
/* data_dst = */ nullptr));
|
||||
#else
|
||||
// TODO(crbug.com/106449): Support custom formats on other platforms.
|
||||
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
|
||||
ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste,
|
||||
/* data_dst = */ nullptr));
|
||||
std::string text_result;
|
||||
this->clipboard().ReadAsciiText(ClipboardBuffer::kCopyPaste,
|
||||
/* data_dst = */ nullptr, &text_result);
|
||||
EXPECT_EQ(text_result, text);
|
||||
// Windows will automatically convert CF_TEXT to its UNICODE version.
|
||||
EXPECT_TRUE(this->clipboard().IsFormatAvailable(
|
||||
ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste,
|
||||
/* data_dst = */ nullptr));
|
||||
std::u16string text_result16;
|
||||
this->clipboard().ReadText(ClipboardBuffer::kCopyPaste,
|
||||
/* data_dst = */ nullptr, &text_result16);
|
||||
EXPECT_EQ(text_result16, base::ASCIIToUTF16(text));
|
||||
#endif // defined(OS_WIN)
|
||||
EXPECT_TRUE(custom_format_names.find(kFormatString) !=
|
||||
custom_format_names.end());
|
||||
std::string platform_specific_result;
|
||||
this->clipboard().ReadData(
|
||||
ClipboardFormatType::GetCustomPlatformType(kFormatString),
|
||||
/* data_dst = */ nullptr, &platform_specific_result);
|
||||
this->clipboard().ReadData(ClipboardFormatType::CustomPlatformType(
|
||||
custom_format_names[kFormatString]),
|
||||
/* data_dst = */ nullptr,
|
||||
&platform_specific_result);
|
||||
EXPECT_EQ(platform_specific_result, kPlatformSpecificText);
|
||||
}
|
||||
#endif // defined(OS_WIN) || defined(USE_X11)
|
||||
|
@ -354,13 +354,16 @@ ClipboardWin::ReadAvailablePlatformSpecificFormatNames(
|
||||
if (!clipboard.Acquire(GetClipboardWindow()))
|
||||
return {};
|
||||
|
||||
// Check if we have any custom formats in the clipboard.
|
||||
std::map<std::string, std::string> custom_format_names =
|
||||
ExtractCustomPlatformNames(buffer, data_dst);
|
||||
for (const auto& items : custom_format_names) {
|
||||
types.push_back(base::ASCIIToUTF16(items.first));
|
||||
}
|
||||
UINT cf_format = 0;
|
||||
cf_format = ::EnumClipboardFormats(cf_format);
|
||||
while (cf_format) {
|
||||
std::string type_name = ClipboardFormatType(cf_format).GetName();
|
||||
// Search for custom types if we couldn't find a standard format.
|
||||
if (type_name.empty())
|
||||
type_name = ClipboardFormatType(cf_format).GetCustomPlatformName();
|
||||
if (!type_name.empty())
|
||||
types.push_back(base::ASCIIToUTF16(type_name));
|
||||
cf_format = ::EnumClipboardFormats(cf_format);
|
||||
|
@ -6,8 +6,10 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/json/json_writer.h"
|
||||
#include "base/pickle.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/values.h"
|
||||
#include "net/base/escape.h"
|
||||
#include "ui/base/clipboard/clipboard_format_type.h"
|
||||
#include "ui/base/clipboard/clipboard_metrics.h"
|
||||
@ -25,6 +27,19 @@ ScopedClipboardWriter::ScopedClipboardWriter(
|
||||
ScopedClipboardWriter::~ScopedClipboardWriter() {
|
||||
static constexpr size_t kMaxRepresentations = 1 << 12;
|
||||
DCHECK(platform_representations_.size() < kMaxRepresentations);
|
||||
// If the metadata format type is not empty then create a JSON payload and
|
||||
// write to the clipboard.
|
||||
if (!registered_formats_.empty()) {
|
||||
std::string custom_format_json;
|
||||
base::Value registered_formats_value(base::Value::Type::DICTIONARY);
|
||||
for (const auto& item : registered_formats_)
|
||||
registered_formats_value.SetStringKey(item.first, item.second);
|
||||
base::JSONWriter::Write(registered_formats_value, &custom_format_json);
|
||||
Clipboard::ObjectMapParams parameters;
|
||||
parameters.push_back(Clipboard::ObjectMapParam(custom_format_json.begin(),
|
||||
custom_format_json.end()));
|
||||
objects_[Clipboard::PortableFormat::kWebCustomFormatMap] = parameters;
|
||||
}
|
||||
if (!objects_.empty() || !platform_representations_.empty()) {
|
||||
Clipboard::GetForCurrentThread()->WritePortableAndPlatformRepresentations(
|
||||
buffer_, objects_, std::move(platform_representations_),
|
||||
@ -171,15 +186,39 @@ void ScopedClipboardWriter::WritePickledData(
|
||||
void ScopedClipboardWriter::WriteData(const std::u16string& format,
|
||||
mojo_base::BigBuffer data) {
|
||||
RecordWrite(ClipboardFormatMetric::kData);
|
||||
platform_representations_.push_back(
|
||||
{base::UTF16ToASCII(format), std::move(data)});
|
||||
// Windows / X11 clipboards enter an unrecoverable state after registering
|
||||
// some amount of unique formats, and there's no way to un-register these
|
||||
// formats. For these clipboards, use a conservative limit to avoid
|
||||
// registering too many formats, as:
|
||||
// (1) Other native applications may also register clipboard formats.
|
||||
// (2) Malicious sites can write more than the hard limit defined on
|
||||
// Windows(16k). (3) Chrome also registers other clipboard formats.
|
||||
//
|
||||
// There will be a custom format map which contains a JSON payload that will
|
||||
// have a mapping of custom format MIME type to web custom format.
|
||||
// There can only be 100 custom format per write and it will be
|
||||
// registered when the web authors request for a custom format.
|
||||
static constexpr int kMaxRegisteredFormats = 100;
|
||||
if (counter_ >= kMaxRegisteredFormats)
|
||||
return;
|
||||
std::string format_in_ascii = base::UTF16ToASCII(format);
|
||||
if (registered_formats_.find(format_in_ascii) == registered_formats_.end()) {
|
||||
std::string web_custom_format_string =
|
||||
ClipboardFormatType::WebCustomFormatName(counter_);
|
||||
registered_formats_[format_in_ascii] = web_custom_format_string;
|
||||
counter_++;
|
||||
platform_representations_.push_back(
|
||||
{web_custom_format_string, std::move(data)});
|
||||
}
|
||||
}
|
||||
|
||||
void ScopedClipboardWriter::Reset() {
|
||||
objects_.clear();
|
||||
platform_representations_.clear();
|
||||
registered_formats_.clear();
|
||||
bitmap_.reset();
|
||||
confidential_ = false;
|
||||
counter_ = 0;
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "base/component_export.h"
|
||||
#include "base/containers/flat_map.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "ui/base/clipboard/clipboard.h"
|
||||
#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
|
||||
@ -94,6 +95,9 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ScopedClipboardWriter {
|
||||
Clipboard::ObjectMap objects_;
|
||||
|
||||
std::vector<Clipboard::PlatformRepresentation> platform_representations_;
|
||||
// Keeps track of the unique custom formats registered in the clipboard.
|
||||
base::flat_map<std::string, std::string> registered_formats_;
|
||||
int counter_ = 0;
|
||||
|
||||
// The type is set at construction, and can be changed before committing.
|
||||
const ClipboardBuffer buffer_;
|
||||
|
@ -142,11 +142,15 @@ TestClipboard::ReadAvailablePlatformSpecificFormatNames(
|
||||
const auto& data = store.data;
|
||||
std::vector<std::u16string> types;
|
||||
types.reserve(data.size());
|
||||
std::map<std::string, std::string> custom_format_names =
|
||||
ExtractCustomPlatformNames(buffer, data_dst);
|
||||
for (const auto& item : custom_format_names)
|
||||
types.push_back(base::UTF8ToUTF16(item.first));
|
||||
for (const auto& it : data) {
|
||||
std::string format_type = it.first.GetName();
|
||||
if (format_type.empty())
|
||||
format_type = it.first.GetCustomPlatformName();
|
||||
types.push_back(base::UTF8ToUTF16(format_type));
|
||||
if (!format_type.empty()) {
|
||||
types.push_back(base::UTF8ToUTF16(format_type));
|
||||
}
|
||||
}
|
||||
|
||||
return types;
|
||||
@ -314,6 +318,7 @@ void TestClipboard::WritePortableAndPlatformRepresentations(
|
||||
std::unique_ptr<DataTransferEndpoint> data_src) {
|
||||
Clear(buffer);
|
||||
default_store_buffer_ = buffer;
|
||||
|
||||
DispatchPlatformRepresentations(std::move(platform_representations));
|
||||
for (const auto& kv : objects)
|
||||
DispatchPortableRepresentation(kv.first, kv.second);
|
||||
|
Reference in New Issue
Block a user