diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-10-26 13:57:00 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-11-02 11:31:01 +0000 |
commit | 1943b3c2a1dcee36c233724fc4ee7613d71b9cf6 (patch) | |
tree | 8c1b5f12357025c197da5427ae02cfdc2f3570d6 /chromium/ui/base/clipboard | |
parent | 21ba0c5d4bf8fba15dddd97cd693bad2358b77fd (diff) | |
download | qtwebengine-chromium-1943b3c2a1dcee36c233724fc4ee7613d71b9cf6.tar.gz |
BASELINE: Update Chromium to 94.0.4606.111
Change-Id: I924781584def20fc800bedf6ff41fdb96c438193
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/ui/base/clipboard')
35 files changed, 1473 insertions, 848 deletions
diff --git a/chromium/ui/base/clipboard/BUILD.gn b/chromium/ui/base/clipboard/BUILD.gn index 42d2fb48145..52d88ba0ffb 100644 --- a/chromium/ui/base/clipboard/BUILD.gn +++ b/chromium/ui/base/clipboard/BUILD.gn @@ -78,6 +78,7 @@ component("clipboard") { "clipboard_monitor.h", "clipboard_observer.cc", "clipboard_observer.h", + "clipboard_sequence_number_token.h", "custom_data_helper.cc", "custom_data_helper.h", "scoped_clipboard_writer.cc", diff --git a/chromium/ui/base/clipboard/DIR_METADATA b/chromium/ui/base/clipboard/DIR_METADATA index 185817f957c..7d0a14efdc4 100644 --- a/chromium/ui/base/clipboard/DIR_METADATA +++ b/chromium/ui/base/clipboard/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "Blink>DataTransfer" diff --git a/chromium/ui/base/clipboard/OWNERS b/chromium/ui/base/clipboard/OWNERS index 3520663eec7..894511c6deb 100644 --- a/chromium/ui/base/clipboard/OWNERS +++ b/chromium/ui/base/clipboard/OWNERS @@ -1,7 +1,8 @@ +asully@chromium.org dcheng@chromium.org -huangdarwin@chromium.org +per-file clipboard_*android*=gangwu@chromium.org +per-file clipboard_*mac*=avi@chromium.org per-file clipboard_*ozone*=msisov@igalia.com per-file clipboard_*ozone*=adunaev@igalia.com per-file clipboard_*x11*=thomasanderson@chromium.org - diff --git a/chromium/ui/base/clipboard/README.md b/chromium/ui/base/clipboard/README.md index 51d1a61b624..154b208122d 100644 --- a/chromium/ui/base/clipboard/README.md +++ b/chromium/ui/base/clipboard/README.md @@ -1,18 +1,43 @@ +# Browser Clipboard + Platform-neutral clipboard abstractions, to access platform-specific clipboards (copy/paste) without platform-specific code. +## Clipboard Model + +The clipboard can be thought of as an ordered dictionary keyed on format, with +the value as the payload. This dictionary is implemented and accessed +differently on different operating systems (OS’s). + +## Interfaces + Interfaces include: * `Clipboard`: reading/pasting from the clipboard. * `ScopedClipboardWriter`: writing/copying to the clipboard. * `ClipboardObserver`: notifications of clipboard events. * `ClipboardFormatType`: specifying clipboard formats. +## Platform-specific behavior + While most platform-specific behavior should be abstracted away, some may still be exposed. For some notable platform-specific behavior exposed by these interfaces: * `ClipboardAndroid` has a more limited set of supported formats. * `ClipboardObserver` is only supported on some platforms, as other platforms may require (inefficient) polling to implement. -* `ClipboardX11` supports both the usual clipboard buffer, as well as the - selection (middle-click) paste buffer. +* Every platform may have different combinations of clipboard formats written, + or metadata written, for each clipboard format. For example, text in Windows + is written with a carriage return accompanying newlines, and in Linux requires + multiple MIME types to represent. +* `ClipboardX11` supports both the usual clipboard buffer (CLIPBOARD selection), + as well as the middle-click paste buffer (PRIMARY selection). X11 selections + are documented in more detail in + [X11 documentation](https://tronche.com/gui/x/icccm/sec-2.html#s-2.6.1). * `DataTransferPolicyController` is only currently exercised in ChromeOS. +* `ClipboardWin` and `ClipboardX11` have limits to the amount of registered + clipboard formats. Windows has the smallest limit on the number of formats, at + [16384](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerclipboardformata#remarks), + and explored in + [this article](https://devblogs.microsoft.com/oldnewthing/20150319-00/?p=44433). + After these system resources are exhausted, the underlying OS may be rendered + unusable.
\ No newline at end of file diff --git a/chromium/ui/base/clipboard/clipboard.cc b/chromium/ui/base/clipboard/clipboard.cc index e5aa1deb231..1916e5c82b9 100644 --- a/chromium/ui/base/clipboard/clipboard.cc +++ b/chromium/ui/base/clipboard/clipboard.cc @@ -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::GetType(representation.format), + WriteData(ClipboardFormatType::CustomPlatformType(representation.format), reinterpret_cast<const char*>(representation.data.data()), representation.data.size()); } diff --git a/chromium/ui/base/clipboard/clipboard.h b/chromium/ui/base/clipboard/clipboard.h index 11ce126c3d8..85009c942b8 100644 --- a/chromium/ui/base/clipboard/clipboard.h +++ b/chromium/ui/base/clipboard/clipboard.h @@ -8,6 +8,7 @@ #include <stddef.h> #include <stdint.h> +#include <map> #include <memory> #include <string> #include <vector> @@ -16,8 +17,6 @@ #include "base/compiler_specific.h" #include "base/component_export.h" #include "base/containers/flat_map.h" -#include "base/macros.h" -#include "base/no_destructor.h" #include "base/process/process.h" #include "base/synchronization/lock.h" #include "base/threading/platform_thread.h" @@ -27,6 +26,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/clipboard_sequence_number_token.h" #include "ui/base/clipboard/file_info.h" #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" @@ -69,6 +69,9 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard base::OnceCallback<void(std::u16string title, GURL url)>; using ReadDataCallback = base::OnceCallback<void(std::string result)>; + Clipboard(const Clipboard&) = delete; + Clipboard& operator=(const Clipboard&) = delete; + static bool IsSupportedClipboardBuffer(ClipboardBuffer buffer); // Sets the list of threads that are allowed to access the clipboard. @@ -90,6 +93,8 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard // the main UI thread, but Windows has tricky semantics where there have to // be two clipboards: one that lives on the UI thread and one that lives on // the IO thread. + // + // The return value should not be cached. static Clipboard* GetForCurrentThread(); // Removes and transfers ownership of the current thread's clipboard to the @@ -114,10 +119,11 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard virtual const DataTransferEndpoint* GetSource( ClipboardBuffer buffer) const = 0; - // Returns a sequence number which uniquely identifies clipboard state. - // This can be used to version the data on the clipboard and determine - // whether it has changed. - virtual uint64_t GetSequenceNumber(ClipboardBuffer buffer) const = 0; + // Returns a token which uniquely identifies clipboard state. + // ClipboardSequenceNumberTokens are used since there may be multiple + // ui::Clipboard instances that have the same sequence number. + virtual const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const = 0; // Tests whether the clipboard contains a certain format. virtual bool IsFormatAvailable( @@ -207,7 +213,7 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard virtual void ReadBookmark(const DataTransferEndpoint* data_dst, ReadBookmarkCallback callback) const; - // Reads raw data from the clipboard with the given format type. Stores result + // Reads data from the clipboard with the given format type. Stores result // as a byte vector. virtual void ReadData(const ClipboardFormatType& format, const DataTransferEndpoint* data_dst, @@ -260,13 +266,22 @@ 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 // is defined by ClipboardFormatType. A single PortableFormat might be // represented by several system-specific ClipboardFormatTypes. For example, // on Linux the kText PortableFormat maps to "text/plain", "STRING", and - // several other formats. On windows it maps to CF_UNICODETEXT. + // several other formats. On Windows it maps to CF_UNICODETEXT. // // The order below is the order in which data will be written to the // clipboard, so more specific types must be listed before less specific @@ -274,6 +289,8 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard // clipboard to contain a bitmap, HTML markup representing the image, a URL to // the image, and the image's alt text. Having the types follow this order // maximizes the amount of data that can be extracted by various programs. + // Documentation on motivation for format ordering is also available here: + // https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats#multiple-clipboard-formats enum class PortableFormat { kBitmap, // Bitmap from shared memory. kHtml, @@ -284,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. @@ -307,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>; @@ -328,22 +347,14 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard Clipboard(); virtual ~Clipboard(); - // Write a bunch of objects to the system clipboard. Copies are made of the - // contents of |objects|. Also, adds the source of the data to the clipboard, - // which can be used when we need to restrict the clipboard data between a set - // of confidential documents. The data source maybe passed as nullptr. - virtual void WritePortableRepresentations( + // Write platform & portable formats, in the order of their appearance in + // `platform_representations` & `ObjectMap`. Also, adds the source of the data + // to the clipboard, which can be used when we need to restrict the clipboard + // data between a set of confidential documents. The data source maybe passed + // as nullptr. + virtual void WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) = 0; - - // Write |platform_representations|, in the order of their appearance in - // |platform_representations|. Also, adds the source of the data to the - // clipboard, which can be used when we need to restrict the clipboard data - // between a set of confidential documents. The data source maybe passed as - // nullptr. - virtual void WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) = 0; @@ -411,8 +422,6 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard // Mutex that controls access to |g_clipboard_map|. static base::Lock& ClipboardMapLock(); - - DISALLOW_COPY_AND_ASSIGN(Clipboard); }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_android.cc b/chromium/ui/base/clipboard/clipboard_android.cc index 62e6ef76ac2..3460fd1d40e 100644 --- a/chromium/ui/base/clipboard/clipboard_android.cc +++ b/chromium/ui/base/clipboard/clipboard_android.cc @@ -5,6 +5,7 @@ #include "ui/base/clipboard/clipboard_android.h" #include <algorithm> +#include <cstdint> #include <map> #include <string> #include <utility> @@ -32,6 +33,7 @@ #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" #include "ui/base/ui_base_jni_headers/Clipboard_jni.h" #include "ui/gfx/android/java_bitmap.h" +#include "ui/gfx/codec/png_codec.h" #include "ui/gfx/image/image.h" // TODO:(andrewhayden) Support additional formats in Android: URI, HTML, @@ -67,6 +69,22 @@ constexpr char kPngExtension[] = ".png"; using ReadPngCallback = ClipboardAndroid::ReadPngCallback; using ReadImageCallback = ClipboardAndroid::ReadImageCallback; +// Fetching image data from Java as PNG bytes. +std::vector<uint8_t> GetPngData( + const base::android::ScopedJavaGlobalRef<jobject>& clipboard_manager) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jbyteArray> jimage_data = + Java_Clipboard_getPng(env, clipboard_manager); + if (jimage_data.is_null()) { + return std::vector<uint8_t>(); + } + DCHECK(jimage_data.obj()); + + std::vector<uint8_t> png_data; + JavaByteArrayToByteVector(env, jimage_data, &png_data); + return png_data; +} + // Fetching image data from Java. SkBitmap GetImageData( const base::android::ScopedJavaGlobalRef<jobject>& clipboard_manager) { @@ -86,14 +104,36 @@ SkBitmap GetImageData( return gfx::CreateSkBitmapFromJavaBitmap(java_bitmap); } +// Add a format:jstr pair to map, if jstr is null or is empty, then remove that +// entry. +void JNI_Clipboard_AddMapEntry(JNIEnv* env, + std::map<ClipboardFormatType, std::string>* map, + const ClipboardFormatType& format, + const ScopedJavaLocalRef<jstring>& jstr) { + if (jstr.is_null()) { + map->erase(format); + return; + } + + std::string str = ConvertJavaStringToUTF8(env, jstr.obj()); + if (!str.empty()) { + (*map)[format] = str; + } else { + map->erase(format); + } +} + class ClipboardMap { public: ClipboardMap(); void SetModifiedCallback(ClipboardAndroid::ModifiedCallback cb); void SetJavaSideNativePtr(Clipboard* clipboard); std::string Get(const ClipboardFormatType& format); + void GetPng(ReadPngCallback callback); void GetImage(ReadImageCallback callback); - uint64_t GetSequenceNumber() const; + void DidGetPng(ReadPngCallback callback, std::vector<uint8_t> result); + void DidGetImage(ReadImageCallback callback, const SkBitmap& result); + const ClipboardSequenceNumberToken& GetSequenceNumber() const; base::Time GetLastModifiedTime() const; void ClearLastModifiedTime(); bool HasFormat(const ClipboardFormatType& format); @@ -130,7 +170,7 @@ class ClipboardMap { // This lock is for read/write |map_|. base::Lock lock_; - uint64_t sequence_number_; + ClipboardSequenceNumberToken sequence_number_; base::Time last_modified_time_; ClipboardAndroid::ModifiedCallback modified_cb_; @@ -162,13 +202,65 @@ std::string ClipboardMap::Get(const ClipboardFormatType& format) { return it == map_.end() ? std::string() : it->second; } +void ClipboardMap::GetPng(ReadPngCallback callback) { + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING}, + base::BindOnce(&GetPngData, clipboard_manager_), + base::BindOnce(&ClipboardMap::DidGetPng, base::Unretained(this), + std::move(callback))); +} + void ClipboardMap::GetImage(ReadImageCallback callback) { base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING}, - base::BindOnce(&GetImageData, clipboard_manager_), std::move(callback)); + base::BindOnce(&GetImageData, clipboard_manager_), + base::BindOnce(&ClipboardMap::DidGetImage, base::Unretained(this), + std::move(callback))); +} + +void ClipboardMap::DidGetPng(ReadPngCallback callback, + std::vector<uint8_t> result) { + // GetPngData attempts to read from the Java Clipboard, which sometimes is + // not available (ex. the app is not in focus, such as in unit tests). + if (!result.empty()) { + std::move(callback).Run(std::move(result)); + return; + } + + // Since the The Java Clipboard did not provide a valid bitmap, attempt to + // read from our in-memory clipboard map if the map is up-to-date. + if (map_state_ != MapState::kUpToDate) { + std::move(callback).Run(std::vector<uint8_t>()); + return; + } + std::string png_str = g_map.Get().Get(ClipboardFormatType::PngType()); + std::vector<uint8_t> png_data{png_str.begin(), png_str.end()}; + std::move(callback).Run(png_data); +} + +void ClipboardMap::DidGetImage(ReadImageCallback callback, + const SkBitmap& result) { + // GetImageData attempts to read from the Java Clipboard, which sometimes is + // not available (ex. the app is not in focus, such as in unit tests). + if (!result.isNull()) { + std::move(callback).Run(std::move(result)); + return; + } + + // Since the The Java Clipboard did not provide a valid bitmap, attempt to + // read from our in-memory clipboard map if the map is up-to-date. + if (map_state_ != MapState::kUpToDate) { + std::move(callback).Run(SkBitmap()); + return; + } + std::string png_str = g_map.Get().Get(ClipboardFormatType::PngType()); + SkBitmap bitmap; + gfx::PNGCodec::Decode(reinterpret_cast<const unsigned char*>(png_str.data()), + png_str.size(), &bitmap); + std::move(callback).Run(std::move(bitmap)); } -uint64_t ClipboardMap::GetSequenceNumber() const { +const ClipboardSequenceNumberToken& ClipboardMap::GetSequenceNumber() const { return sequence_number_; } @@ -184,6 +276,12 @@ bool ClipboardMap::HasFormat(const ClipboardFormatType& format) { base::AutoLock lock(lock_); if (map_state_ == MapState::kUpToDate) { // If the 'map_' is up to date, we can just check with it. + // Images can be read if either bitmap or PNG types are available. + if (format == ClipboardFormatType::PngType() || + format == ClipboardFormatType::BitmapType()) { + return base::Contains(map_, ClipboardFormatType::PngType()) || + base::Contains(map_, ClipboardFormatType::BitmapType()); + } return base::Contains(map_, format); } @@ -195,13 +293,14 @@ bool ClipboardMap::HasFormat(const ClipboardFormatType& format) { // up. JNIEnv* env = AttachCurrentThread(); // TODO(crbug.com/1194601): Create a single method for the follow JNI calls. - if (format == ClipboardFormatType::GetPlainTextType()) { + if (format == ClipboardFormatType::PlainTextType()) { return Java_Clipboard_hasCoercedText(env, clipboard_manager_); - } else if (format == ClipboardFormatType::GetHtmlType()) { + } else if (format == ClipboardFormatType::HtmlType()) { return Java_Clipboard_hasHTMLOrStyledText(env, clipboard_manager_); - } else if (format == ClipboardFormatType::GetUrlType()) { + } else if (format == ClipboardFormatType::UrlType()) { return Java_Clipboard_hasUrl(env, clipboard_manager_); - } else if (format == ClipboardFormatType::GetType(kMimeTypeImageURI)) { + } else if (format == ClipboardFormatType::PngType() || + format == ClipboardFormatType::BitmapType()) { return Java_Clipboard_hasImage(env, clipboard_manager_); } @@ -218,16 +317,17 @@ std::vector<ClipboardFormatType> ClipboardMap::GetFormats() { if (map_state_ != MapState::kUpToDate) { JNIEnv* env = AttachCurrentThread(); if (Java_Clipboard_hasCoercedText(env, clipboard_manager_)) { - formats.push_back(ClipboardFormatType::GetPlainTextType()); + formats.push_back(ClipboardFormatType::PlainTextType()); } if (Java_Clipboard_hasHTMLOrStyledText(env, clipboard_manager_)) { - formats.push_back(ClipboardFormatType::GetHtmlType()); + formats.push_back(ClipboardFormatType::HtmlType()); } if (Java_Clipboard_hasUrl(env, clipboard_manager_)) { - formats.push_back(ClipboardFormatType::GetUrlType()); + formats.push_back(ClipboardFormatType::UrlType()); } if (Java_Clipboard_hasImage(env, clipboard_manager_)) { - formats.push_back(ClipboardFormatType::GetBitmapType()); + formats.push_back(ClipboardFormatType::BitmapType()); + formats.push_back(ClipboardFormatType::PngType()); } } @@ -235,10 +335,11 @@ std::vector<ClipboardFormatType> ClipboardMap::GetFormats() { // not synced on any other layer. for (const auto& it : map_) { if (map_state_ != MapState::kUpToDate && - (it.first == ClipboardFormatType::GetPlainTextType() || - it.first == ClipboardFormatType::GetHtmlType() || - it.first == ClipboardFormatType::GetUrlType() || - it.first == ClipboardFormatType::GetType(kMimeTypeImageURI))) { + (it.first == ClipboardFormatType::PlainTextType() || + it.first == ClipboardFormatType::HtmlType() || + it.first == ClipboardFormatType::UrlType() || + it.first == ClipboardFormatType::BitmapType() || + it.first == ClipboardFormatType::PngType())) { continue; } formats.push_back(it.first); @@ -248,7 +349,7 @@ std::vector<ClipboardFormatType> ClipboardMap::GetFormats() { } void ClipboardMap::OnPrimaryClipboardChanged() { - sequence_number_++; + sequence_number_ = ClipboardSequenceNumberToken(); UpdateLastModifiedTime(base::Time::Now()); map_state_ = MapState::kOutOfDate; } @@ -256,7 +357,7 @@ void ClipboardMap::OnPrimaryClipboardChanged() { void ClipboardMap::OnPrimaryClipTimestampInvalidated(int64_t timestamp_ms) { base::Time timestamp = base::Time::FromJavaTime(timestamp_ms); if (GetLastModifiedTime() < timestamp) { - sequence_number_++; + sequence_number_ = ClipboardSequenceNumberToken(); UpdateLastModifiedTime(timestamp); map_state_ = MapState::kOutOfDate; } @@ -272,39 +373,46 @@ void ClipboardMap::Set(const ClipboardFormatType& format, void ClipboardMap::CommitToAndroidClipboard() { JNIEnv* env = AttachCurrentThread(); base::AutoLock lock(lock_); - if (base::Contains(map_, ClipboardFormatType::GetHtmlType())) { + if (base::Contains(map_, ClipboardFormatType::HtmlType())) { // Android's API for storing HTML content on the clipboard requires a plain- // text representation to be available as well. - if (!base::Contains(map_, ClipboardFormatType::GetPlainTextType())) + if (!base::Contains(map_, ClipboardFormatType::PlainTextType())) return; ScopedJavaLocalRef<jstring> html = - ConvertUTF8ToJavaString(env, map_[ClipboardFormatType::GetHtmlType()]); + ConvertUTF8ToJavaString(env, map_[ClipboardFormatType::HtmlType()]); ScopedJavaLocalRef<jstring> text = ConvertUTF8ToJavaString( - env, map_[ClipboardFormatType::GetPlainTextType()]); + env, map_[ClipboardFormatType::PlainTextType()]); DCHECK(html.obj() && text.obj()); Java_Clipboard_setHTMLText(env, clipboard_manager_, html, text); - } else if (base::Contains(map_, ClipboardFormatType::GetPlainTextType())) { + } else if (base::Contains(map_, ClipboardFormatType::PlainTextType())) { ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString( - env, map_[ClipboardFormatType::GetPlainTextType()]); + env, map_[ClipboardFormatType::PlainTextType()]); DCHECK(str.obj()); Java_Clipboard_setText(env, clipboard_manager_, str); - } else if (base::Contains(map_, ClipboardFormatType::GetBitmapType())) { + } else if (base::Contains(map_, ClipboardFormatType::PngType())) { + // Committing the PNG data to the Android clipboard will create an image + // with a corresponding URI. Once this has been created, update the local + // clipboard with this URI. ScopedJavaLocalRef<jbyteArray> image_data = - ToJavaByteArray(env, map_[ClipboardFormatType::GetBitmapType()]); + ToJavaByteArray(env, map_[ClipboardFormatType::PngType()]); ScopedJavaLocalRef<jstring> image_extension = ConvertUTF8ToJavaString(env, kPngExtension); DCHECK(image_data.obj()); + // TODO(crbug.com/1223215) In unit tests, `jimageuri` is empty. Java_Clipboard_setImage(env, clipboard_manager_, image_data, image_extension); + ScopedJavaLocalRef<jstring> jimageuri = + Java_Clipboard_getImageUriString(env, clipboard_manager_); + JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::BitmapType(), + jimageuri); } else { Java_Clipboard_clear(env, clipboard_manager_); - // TODO(huangdarwin): Implement raw clipboard support for arbitrary formats. NOTIMPLEMENTED(); } map_state_ = MapState::kUpToDate; - sequence_number_++; + sequence_number_ = ClipboardSequenceNumberToken(); UpdateLastModifiedTime(base::Time::Now()); } @@ -314,7 +422,7 @@ void ClipboardMap::Clear() { map_.clear(); Java_Clipboard_clear(env, clipboard_manager_); map_state_ = MapState::kUpToDate; - sequence_number_++; + sequence_number_ = ClipboardSequenceNumberToken(); UpdateLastModifiedTime(base::Time::Now()); } @@ -322,25 +430,6 @@ void ClipboardMap::SetLastModifiedTimeWithoutRunningCallback(base::Time time) { last_modified_time_ = time; } -// Add a format:jstr pair to map, if jstr is null or is empty, then remove that -// entry. -void JNI_Clipboard_AddMapEntry(JNIEnv* env, - std::map<ClipboardFormatType, std::string>* map, - const ClipboardFormatType& format, - const ScopedJavaLocalRef<jstring>& jstr) { - if (jstr.is_null()) { - map->erase(format); - return; - } - - std::string str = ConvertJavaStringToUTF8(env, jstr.obj()); - if (!str.empty()) { - (*map)[format] = str; - } else { - map->erase(format); - } -} - void ClipboardMap::UpdateLastModifiedTime(base::Time time) { last_modified_time_ = time; // |modified_cb_| may be null in tests. @@ -366,14 +455,12 @@ void ClipboardMap::UpdateFromAndroidClipboard() { ScopedJavaLocalRef<jstring> jimageuri = Java_Clipboard_getImageUriString(env, clipboard_manager_); - JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::GetPlainTextType(), + JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::PlainTextType(), jtext); - JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::GetHtmlType(), - jhtml); - JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::GetUrlType(), - jurl); - JNI_Clipboard_AddMapEntry( - env, &map_, ClipboardFormatType::GetType(kMimeTypeImageURI), jimageuri); + JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::HtmlType(), jhtml); + JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::UrlType(), jurl); + JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::BitmapType(), + jimageuri); map_state_ = MapState::kUpToDate; } @@ -433,7 +520,7 @@ DataTransferEndpoint* ClipboardAndroid::GetSource( return nullptr; } -uint64_t ClipboardAndroid::GetSequenceNumber( +const ClipboardSequenceNumberToken& ClipboardAndroid::GetSequenceNumber( ClipboardBuffer /* buffer */) const { DCHECK(CalledOnValidThread()); return g_map.Get().GetSequenceNumber(); @@ -447,11 +534,6 @@ bool ClipboardAndroid::IsFormatAvailable( const DataTransferEndpoint* data_dst) const { DCHECK(CalledOnValidThread()); DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - - if (format == ClipboardFormatType::GetBitmapType()) { - return g_map.Get().HasFormat( - ClipboardFormatType::GetType(kMimeTypeImageURI)); - } return g_map.Get().HasFormat(format); } @@ -475,17 +557,20 @@ void ClipboardAndroid::ReadAvailableTypes( // would be nice to ask the ClipboardMap to enumerate the types it supports, // rather than hardcode the list here. - if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer, - data_dst)) + if (IsFormatAvailable(ClipboardFormatType::PlainTextType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeText)); - if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst)) + if (IsFormatAvailable(ClipboardFormatType::HtmlType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeHTML)); - if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer, data_dst)) + // We can read images from either the Android clipboard or the local map. + if (IsFormatAvailable(ClipboardFormatType::BitmapType(), buffer, data_dst) || + IsFormatAvailable(ClipboardFormatType::PngType(), buffer, data_dst)) { + types->push_back(base::UTF8ToUTF16(kMimeTypeImageURI)); types->push_back(base::UTF8ToUTF16(kMimeTypePNG)); + } // these formats aren't supported by the ClipboardMap currently, but might // be one day? - if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst)) + if (IsFormatAvailable(ClipboardFormatType::RtfType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeRTF)); } @@ -526,7 +611,7 @@ void ClipboardAndroid::ReadAsciiText(ClipboardBuffer buffer, DCHECK(CalledOnValidThread()); DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); RecordRead(ClipboardFormatMetric::kText); - *result = g_map.Get().Get(ClipboardFormatType::GetPlainTextType()); + *result = g_map.Get().Get(ClipboardFormatType::PlainTextType()); } // |src_url| isn't really used. It is only implemented in Windows. @@ -544,7 +629,7 @@ void ClipboardAndroid::ReadHTML(ClipboardBuffer buffer, if (src_url) src_url->clear(); - std::string input = g_map.Get().Get(ClipboardFormatType::GetHtmlType()); + std::string input = g_map.Get().Get(ClipboardFormatType::HtmlType()); *markup = base::UTF8ToUTF16(input); *fragment_start = 0; @@ -558,7 +643,7 @@ void ClipboardAndroid::ReadSvg(ClipboardBuffer buffer, std::u16string* result) const { DCHECK(CalledOnValidThread()); DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - std::string utf8 = g_map.Get().Get(ClipboardFormatType::GetSvgType()); + std::string utf8 = g_map.Get().Get(ClipboardFormatType::SvgType()); *result = base::UTF8ToUTF16(utf8); } @@ -577,8 +662,9 @@ void ClipboardAndroid::ReadPng(ClipboardBuffer buffer, const DataTransferEndpoint* data_dst, ReadPngCallback callback) const { DCHECK(CalledOnValidThread()); - // TODO(crbug.com/1201018): Implement this. - NOTIMPLEMENTED(); + DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); + RecordRead(ClipboardFormatMetric::kPng); + g_map.Get().GetPng(std::move(callback)); } // |data_dst| is not used. It's only passed to be consistent with other @@ -618,7 +704,7 @@ void ClipboardAndroid::ReadBookmark(const DataTransferEndpoint* data_dst, std::string* url) const { DCHECK(CalledOnValidThread()); RecordRead(ClipboardFormatMetric::kBookmark); - *url = g_map.Get().Get(ClipboardFormatType::GetUrlType()); + *url = g_map.Get().Get(ClipboardFormatType::UrlType()); } // |data_dst| is not used. It's only passed to be consistent with other @@ -644,24 +730,9 @@ void ClipboardAndroid::ClearLastModifiedTime() { // Main entry point used to write several values in the clipboard. // |data_src| is not used. It's only passed to be consistent with other // platforms. -void ClipboardAndroid::WritePortableRepresentations( +void ClipboardAndroid::WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - g_map.Get().Clear(); - - for (const auto& object : objects) - DispatchPortableRepresentation(object.first, object.second); - - g_map.Get().CommitToAndroidClipboard(); -} - -// |data_src| is not used. It's only passed to be consistent with other -// platforms. -void ClipboardAndroid::WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) { DCHECK(CalledOnValidThread()); @@ -669,12 +740,14 @@ void ClipboardAndroid::WritePlatformRepresentations( g_map.Get().Clear(); DispatchPlatformRepresentations(std::move(platform_representations)); + for (const auto& object : objects) + DispatchPortableRepresentation(object.first, object.second); g_map.Get().CommitToAndroidClipboard(); } void ClipboardAndroid::WriteText(const char* text_data, size_t text_len) { - g_map.Get().Set(ClipboardFormatType::GetPlainTextType(), + g_map.Get().Set(ClipboardFormatType::PlainTextType(), std::string(text_data, text_len)); } @@ -682,12 +755,12 @@ void ClipboardAndroid::WriteHTML(const char* markup_data, size_t markup_len, const char* url_data, size_t url_len) { - g_map.Get().Set(ClipboardFormatType::GetHtmlType(), + g_map.Get().Set(ClipboardFormatType::HtmlType(), std::string(markup_data, markup_len)); } void ClipboardAndroid::WriteSvg(const char* markup_data, size_t markup_len) { - g_map.Get().Set(ClipboardFormatType::GetSvgType(), + g_map.Get().Set(ClipboardFormatType::SvgType(), std::string(markup_data, markup_len)); } @@ -705,15 +778,14 @@ void ClipboardAndroid::WriteBookmark(const char* title_data, size_t title_len, const char* url_data, size_t url_len) { - g_map.Get().Set(ClipboardFormatType::GetUrlType(), + g_map.Get().Set(ClipboardFormatType::UrlType(), std::string(url_data, url_len)); } // Write an extra flavor that signifies WebKit was the last to modify the // pasteboard. This flavor has no data. void ClipboardAndroid::WriteWebSmartPaste() { - g_map.Get().Set(ClipboardFormatType::GetWebKitSmartPasteType(), - std::string()); + g_map.Get().Set(ClipboardFormatType::WebKitSmartPasteType(), std::string()); } // Encoding SkBitmap to PNG data. Then, |g_map| can commit the PNG data to @@ -723,7 +795,7 @@ void ClipboardAndroid::WriteBitmap(const SkBitmap& sk_bitmap) { gfx::Image::CreateFrom1xBitmap(sk_bitmap).As1xPNGBytes(); std::string packed(image_memory->front_as<char>(), image_memory->size()); - g_map.Get().Set(ClipboardFormatType::GetBitmapType(), packed); + g_map.Get().Set(ClipboardFormatType::PngType(), packed); } void ClipboardAndroid::WriteData(const ClipboardFormatType& format, diff --git a/chromium/ui/base/clipboard/clipboard_android.h b/chromium/ui/base/clipboard/clipboard_android.h index 870ee348b14..ee7865653c6 100644 --- a/chromium/ui/base/clipboard/clipboard_android.h +++ b/chromium/ui/base/clipboard/clipboard_android.h @@ -14,17 +14,21 @@ #include "base/android/scoped_java_ref.h" #include "base/callback_forward.h" #include "base/component_export.h" -#include "base/macros.h" #include "base/time/time.h" namespace ui { +// Documentation on the underlying Android API this ultimately abstracts is +// available at https://developer.android.com/guide/topics/text/copy-paste. class ClipboardAndroid : public Clipboard { public: // Callback called whenever the clipboard is modified. The parameter // represents the time of the modification. using ModifiedCallback = base::RepeatingCallback<void(base::Time)>; + ClipboardAndroid(const ClipboardAndroid&) = delete; + ClipboardAndroid& operator=(const ClipboardAndroid&) = delete; + // Called by Java when the Java Clipboard is notified that the clipboard has // changed. void OnPrimaryClipChanged(JNIEnv* env, @@ -59,7 +63,8 @@ class ClipboardAndroid : public Clipboard { // Clipboard overrides: void OnPreShutdown() override; DataTransferEndpoint* GetSource(ClipboardBuffer buffer) const override; - uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override; + const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const override; bool IsFormatAvailable(const ClipboardFormatType& format, ClipboardBuffer buffer, const DataTransferEndpoint* data_dst) const override; @@ -109,12 +114,9 @@ class ClipboardAndroid : public Clipboard { std::string* result) const override; base::Time GetLastModifiedTime() const override; void ClearLastModifiedTime() override; - void WritePortableRepresentations( + void WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) override; - void WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) override; void WriteText(const char* text_data, size_t text_len) override; @@ -134,8 +136,6 @@ class ClipboardAndroid : public Clipboard { void WriteData(const ClipboardFormatType& format, const char* data_data, size_t data_len) override; - - DISALLOW_COPY_AND_ASSIGN(ClipboardAndroid); }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_android_test_support.cc b/chromium/ui/base/clipboard/clipboard_android_test_support.cc index b7f126afcfb..d821fdd28be 100644 --- a/chromium/ui/base/clipboard/clipboard_android_test_support.cc +++ b/chromium/ui/base/clipboard/clipboard_android_test_support.cc @@ -33,10 +33,10 @@ jboolean JNI_ClipboardAndroidTestSupport_NativeWriteHtml( clipboard_writer.WriteText(html_text); } auto* clipboard = Clipboard::GetForCurrentThread(); - return clipboard->IsFormatAvailable(ClipboardFormatType::GetHtmlType(), + return clipboard->IsFormatAvailable(ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr) && - clipboard->IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), + clipboard->IsFormatAvailable(ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr); } @@ -48,14 +48,14 @@ jboolean JNI_ClipboardAndroidTestSupport_NativeClipboardContains( // ClipboardManager. This should update the native side of the clipboard as // well as the Android side. auto* clipboard = Clipboard::GetForCurrentThread(); - if (clipboard->IsFormatAvailable(ClipboardFormatType::GetHtmlType(), + if (clipboard->IsFormatAvailable(ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)) { LOG(ERROR) << "HTML still in clipboard."; return false; } - if (!clipboard->IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), + if (!clipboard->IsFormatAvailable(ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)) { LOG(ERROR) << "Plain text not in clipboard."; diff --git a/chromium/ui/base/clipboard/clipboard_data.cc b/chromium/ui/base/clipboard/clipboard_data.cc index 0bc1e93ec74..f78d9f67b91 100644 --- a/chromium/ui/base/clipboard/clipboard_data.cc +++ b/chromium/ui/base/clipboard/clipboard_data.cc @@ -58,6 +58,31 @@ bool ClipboardData::operator!=(const ClipboardData& that) const { return !(*this == that); } +absl::optional<size_t> ClipboardData::size() const { + if (format_ & static_cast<int>(ClipboardInternalFormat::kFilenames)) + return absl::nullopt; + size_t total_size = 0; + if (format_ & static_cast<int>(ClipboardInternalFormat::kText)) + total_size += text_.size(); + if (format_ & static_cast<int>(ClipboardInternalFormat::kHtml)) { + total_size += markup_data_.size(); + total_size += url_.size(); + } + if (format_ & static_cast<int>(ClipboardInternalFormat::kSvg)) + total_size += svg_data_.size(); + if (format_ & static_cast<int>(ClipboardInternalFormat::kRtf)) + total_size += rtf_data_.size(); + if (format_ & static_cast<int>(ClipboardInternalFormat::kBookmark)) { + total_size += bookmark_title_.size(); + total_size += bookmark_url_.size(); + } + if (format_ & static_cast<int>(ClipboardInternalFormat::kPng)) + total_size += png_.size(); + if (format_ & static_cast<int>(ClipboardInternalFormat::kCustom)) + total_size += custom_data_data_.size(); + return total_size; +} + void ClipboardData::SetPngData(std::vector<uint8_t> png) { png_ = std::move(png); format_ |= static_cast<int>(ClipboardInternalFormat::kPng); diff --git a/chromium/ui/base/clipboard/clipboard_data.h b/chromium/ui/base/clipboard/clipboard_data.h index 3f4a550655a..3bf22dbe16a 100644 --- a/chromium/ui/base/clipboard/clipboard_data.h +++ b/chromium/ui/base/clipboard/clipboard_data.h @@ -9,6 +9,7 @@ #include <vector> #include "base/component_export.h" +#include "third_party/abseil-cpp/absl/types/optional.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" @@ -46,6 +47,10 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardData { // Bitmask of ClipboardInternalFormat types. int format() const { return format_; } + // Returns the total size of the data in clipboard, or absl::nullopt if it + // can't be determined. + absl::optional<size_t> size() const; + const std::string& text() const { return text_; } void set_text(const std::string& text) { text_ = text; diff --git a/chromium/ui/base/clipboard/clipboard_format_type.h b/chromium/ui/base/clipboard/clipboard_format_type.h index 03451964918..e3ca57b66ea 100644 --- a/chromium/ui/base/clipboard/clipboard_format_type.h +++ b/chromium/ui/base/clipboard/clipboard_format_type.h @@ -14,7 +14,7 @@ #include "build/build_config.h" #if defined(OS_WIN) -#include <objidl.h> +#include "base/win/windows_types.h" #endif #if defined(OS_APPLE) @@ -45,48 +45,57 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) ClipboardFormatType { std::string Serialize() const; static ClipboardFormatType Deserialize(const std::string& serialization); - // 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". + // Gets the ClipboardFormatType corresponding to the standard formats. static ClipboardFormatType GetType(const std::string& format_string); // Get format identifiers for various types. - static const ClipboardFormatType& GetFilenamesType(); - static const ClipboardFormatType& GetUrlType(); - static const ClipboardFormatType& GetPlainTextType(); - static const ClipboardFormatType& GetWebKitSmartPasteType(); + static const ClipboardFormatType& FilenamesType(); + static const ClipboardFormatType& UrlType(); + static const ClipboardFormatType& PlainTextType(); + static const ClipboardFormatType& WebKitSmartPasteType(); // Win: MS HTML Format, Other: Generic HTML format - static const ClipboardFormatType& GetHtmlType(); - static const ClipboardFormatType& GetSvgType(); - static const ClipboardFormatType& GetRtfType(); - static const ClipboardFormatType& GetPngType(); + static const ClipboardFormatType& HtmlType(); + static const ClipboardFormatType& SvgType(); + static const ClipboardFormatType& RtfType(); + static const ClipboardFormatType& PngType(); // TODO(crbug.com/1201018): Remove this type. - static const ClipboardFormatType& GetBitmapType(); - static const ClipboardFormatType& GetWebCustomDataType(); + static const ClipboardFormatType& BitmapType(); + static const ClipboardFormatType& WebCustomDataType(); #if defined(OS_WIN) // ANSI formats. Only Windows differentiates between ANSI and UNICODE formats // in ClipboardFormatType. Reference: // https://docs.microsoft.com/en-us/windows/win32/learnwin32/working-with-strings - static const ClipboardFormatType& GetUrlAType(); - static const ClipboardFormatType& GetPlainTextAType(); - static const ClipboardFormatType& GetFilenameAType(); + static const ClipboardFormatType& UrlAType(); + static const ClipboardFormatType& PlainTextAType(); + static const ClipboardFormatType& FilenameAType(); // Firefox text/html - static const ClipboardFormatType& GetTextHtmlType(); - static const ClipboardFormatType& GetCFHDropType(); - static const ClipboardFormatType& GetFileDescriptorAType(); - static const ClipboardFormatType& GetFileDescriptorType(); - static const ClipboardFormatType& GetFileContentZeroType(); - static const ClipboardFormatType& GetFileContentAtIndexType(LONG index); - static const ClipboardFormatType& GetFilenameType(); - static const ClipboardFormatType& GetIDListType(); - static const ClipboardFormatType& GetMozUrlType(); + static const ClipboardFormatType& TextHtmlType(); + static const ClipboardFormatType& CFHDropType(); + static const ClipboardFormatType& FileDescriptorAType(); + static const ClipboardFormatType& FileDescriptorType(); + static const ClipboardFormatType& FileContentZeroType(); + static const ClipboardFormatType& FileContentAtIndexType(LONG index); + static const ClipboardFormatType& FilenameType(); + static const ClipboardFormatType& IDListType(); + static const ClipboardFormatType& MozUrlType(); #endif + // 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 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; @@ -94,7 +103,7 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) ClipboardFormatType { // if the format isn't found. std::string GetName() const; #if defined(OS_WIN) - const FORMATETC& ToFormatEtc() const { return data_; } + const FORMATETC& ToFormatEtc() const { return *ChromeToWindowsType(&data_); } #elif defined(OS_APPLE) NSString* ToNSString() const { return data_; } // Custom copy and assignment constructor to handle NSString. @@ -126,11 +135,11 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) ClipboardFormatType { // an index into the data (the first file corresponds to index 0). This // function returns a map of index to CFSTR_FILECONTENTS clipboard format // type. - static std::map<LONG, ClipboardFormatType>& GetFileContentTypeMap(); + static std::map<LONG, ClipboardFormatType>& FileContentTypeMap(); // FORMATETC: // https://docs.microsoft.com/en-us/windows/desktop/com/the-formatetc-structure - FORMATETC data_; + CHROME_FORMATETC data_; #elif defined(USE_AURA) || defined(OS_ANDROID) || defined(OS_FUCHSIA) explicit ClipboardFormatType(const std::string& native_format); std::string data_; diff --git a/chromium/ui/base/clipboard/clipboard_format_type_android.cc b/chromium/ui/base/clipboard/clipboard_format_type_android.cc index ded6f0c1d1a..7af255bc24c 100644 --- a/chromium/ui/base/clipboard/clipboard_format_type_android.cc +++ b/chromium/ui/base/clipboard/clipboard_format_type_android.cc @@ -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,6 +41,31 @@ bool ClipboardFormatType::operator==(const ClipboardFormatType& other) const { return data_ == other.data_; } +// 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); +} + +// static +const ClipboardFormatType& ClipboardFormatType::WebCustomFormatMap() { + static base::NoDestructor<ClipboardFormatType> type( + ClipboardFormatType::WebCustomFormatMapName()); + return *type; +} + // Various predefined ClipboardFormatTypes. // static @@ -47,61 +75,62 @@ ClipboardFormatType ClipboardFormatType::GetType( } // static -const ClipboardFormatType& ClipboardFormatType::GetFilenamesType() { +const ClipboardFormatType& ClipboardFormatType::FilenamesType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeURIList); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetUrlType() { +const ClipboardFormatType& ClipboardFormatType::UrlType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeMozillaURL); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetPlainTextType() { +const ClipboardFormatType& ClipboardFormatType::PlainTextType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeText); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetWebKitSmartPasteType() { +const ClipboardFormatType& ClipboardFormatType::WebKitSmartPasteType() { static base::NoDestructor<ClipboardFormatType> type( kMimeTypeWebkitSmartPaste); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetHtmlType() { +const ClipboardFormatType& ClipboardFormatType::HtmlType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeHTML); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetSvgType() { +const ClipboardFormatType& ClipboardFormatType::SvgType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeSvg); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetRtfType() { +const ClipboardFormatType& ClipboardFormatType::RtfType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeRTF); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetPngType() { +const ClipboardFormatType& ClipboardFormatType::PngType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypePNG); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetBitmapType() { - return ClipboardFormatType::GetPngType(); +const ClipboardFormatType& ClipboardFormatType::BitmapType() { + static base::NoDestructor<ClipboardFormatType> type(kMimeTypeImageURI); + return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetWebCustomDataType() { +const ClipboardFormatType& ClipboardFormatType::WebCustomDataType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeWebCustomData); return *type; } diff --git a/chromium/ui/base/clipboard/clipboard_format_type_aura.cc b/chromium/ui/base/clipboard/clipboard_format_type_aura.cc index a47fd9ae246..078fa98592f 100644 --- a/chromium/ui/base/clipboard/clipboard_format_type_aura.cc +++ b/chromium/ui/base/clipboard/clipboard_format_type_aura.cc @@ -4,14 +4,17 @@ #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 { -// TODO(huangdarwin): Investigate creating a new clipboard_format_type_x11 as a -// wrapper around an X11 ::Atom. This wasn't possible in the past, because unit -// tests spawned a new X11 server for each test, meaning Atom numeric values -// didn't persist across tests. +// TODO(crbug.com/1123230): Investigate creating a new clipboard_format_type_x11 +// as a wrapper around an X11 ::Atom. This wasn't possible in the past, because +// unit tests spawned a new X11 server for each test, meaning Atom numeric +// values didn't persist across tests. ClipboardFormatType::ClipboardFormatType() = default; ClipboardFormatType::~ClipboardFormatType() = default; @@ -41,6 +44,31 @@ bool ClipboardFormatType::operator==(const ClipboardFormatType& other) const { return data_ == other.data_; } +// static +ClipboardFormatType ClipboardFormatType::CustomPlatformType( + const std::string& format_string) { + DCHECK(base::IsStringASCII(format_string)); + return ClipboardFormatType::Deserialize(format_string); +} + +// 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. // static @@ -50,61 +78,61 @@ ClipboardFormatType ClipboardFormatType::GetType( } // static -const ClipboardFormatType& ClipboardFormatType::GetFilenamesType() { +const ClipboardFormatType& ClipboardFormatType::FilenamesType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeURIList); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetUrlType() { +const ClipboardFormatType& ClipboardFormatType::UrlType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeMozillaURL); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetPlainTextType() { +const ClipboardFormatType& ClipboardFormatType::PlainTextType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeText); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetHtmlType() { +const ClipboardFormatType& ClipboardFormatType::HtmlType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeHTML); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetSvgType() { +const ClipboardFormatType& ClipboardFormatType::SvgType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeSvg); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetRtfType() { +const ClipboardFormatType& ClipboardFormatType::RtfType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeRTF); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetPngType() { +const ClipboardFormatType& ClipboardFormatType::PngType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypePNG); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetBitmapType() { - return ClipboardFormatType::GetPngType(); +const ClipboardFormatType& ClipboardFormatType::BitmapType() { + return ClipboardFormatType::PngType(); } // static -const ClipboardFormatType& ClipboardFormatType::GetWebKitSmartPasteType() { +const ClipboardFormatType& ClipboardFormatType::WebKitSmartPasteType() { static base::NoDestructor<ClipboardFormatType> type( kMimeTypeWebkitSmartPaste); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetWebCustomDataType() { +const ClipboardFormatType& ClipboardFormatType::WebCustomDataType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeWebCustomData); return *type; } diff --git a/chromium/ui/base/clipboard/clipboard_format_type_mac.mm b/chromium/ui/base/clipboard/clipboard_format_type_mac.mm index 719582aa1da..f370fc8745e 100644 --- a/chromium/ui/base/clipboard/clipboard_format_type_mac.mm +++ b/chromium/ui/base/clipboard/clipboard_format_type_mac.mm @@ -6,12 +6,17 @@ #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" namespace ui { // ClipboardFormatType implementation. +// MacOS formats are implemented via Uniform Type Identifiers, documented here: +// https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/UniformTypeIdentifier.html#//apple_ref/doc/uid/TP40008195-CH60 ClipboardFormatType::ClipboardFormatType() : data_(nil) {} ClipboardFormatType::ClipboardFormatType(NSString* native_format) @@ -55,6 +60,30 @@ bool ClipboardFormatType::operator<(const ClipboardFormatType& other) const { return [data_ compare:other.data_] == NSOrderedAscending; } +// static +std::string ClipboardFormatType::WebCustomFormatName(int index) { + return base::StrCat({"com.web.custom.format", base::NumberToString(index)}); +} + +// 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. // static @@ -64,60 +93,60 @@ ClipboardFormatType ClipboardFormatType::GetType( } // static -const ClipboardFormatType& ClipboardFormatType::GetFilenamesType() { +const ClipboardFormatType& ClipboardFormatType::FilenamesType() { static base::NoDestructor<ClipboardFormatType> type(NSFilenamesPboardType); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetUrlType() { +const ClipboardFormatType& ClipboardFormatType::UrlType() { static base::NoDestructor<ClipboardFormatType> type(NSURLPboardType); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetPlainTextType() { +const ClipboardFormatType& ClipboardFormatType::PlainTextType() { static base::NoDestructor<ClipboardFormatType> type(NSPasteboardTypeString); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetHtmlType() { +const ClipboardFormatType& ClipboardFormatType::HtmlType() { static base::NoDestructor<ClipboardFormatType> type(NSHTMLPboardType); return *type; } -const ClipboardFormatType& ClipboardFormatType::GetSvgType() { +const ClipboardFormatType& ClipboardFormatType::SvgType() { static base::NoDestructor<ClipboardFormatType> type(kImageSvg); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetRtfType() { +const ClipboardFormatType& ClipboardFormatType::RtfType() { static base::NoDestructor<ClipboardFormatType> type(NSRTFPboardType); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetPngType() { +const ClipboardFormatType& ClipboardFormatType::PngType() { static base::NoDestructor<ClipboardFormatType> type(NSPasteboardTypePNG); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetBitmapType() { +const ClipboardFormatType& ClipboardFormatType::BitmapType() { static base::NoDestructor<ClipboardFormatType> type(NSTIFFPboardType); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetWebKitSmartPasteType() { +const ClipboardFormatType& ClipboardFormatType::WebKitSmartPasteType() { static base::NoDestructor<ClipboardFormatType> type(kWebSmartPastePboardType); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetWebCustomDataType() { +const ClipboardFormatType& ClipboardFormatType::WebCustomDataType() { static base::NoDestructor<ClipboardFormatType> type(kWebCustomDataPboardType); return *type; } diff --git a/chromium/ui/base/clipboard/clipboard_format_type_win.cc b/chromium/ui/base/clipboard/clipboard_format_type_win.cc index 8834346f7ae..b700f40eba7 100644 --- a/chromium/ui/base/clipboard/clipboard_format_type_win.cc +++ b/chromium/ui/base/clipboard/clipboard_format_type_win.cc @@ -9,74 +9,19 @@ #include "base/containers/flat_map.h" #include "base/memory/ptr_util.h" -#include "base/metrics/histogram_functions.h" #include "base/no_destructor.h" #include "base/notreached.h" +#include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" - -namespace { - -const base::flat_map<UINT, std::string>& PredefinedFormatToNameMap() { - // These formats are described in winuser.h and - // https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats - static const base::NoDestructor<base::flat_map<UINT, std::string>> - format_to_name({ - {CF_TEXT, "CF_TEXT"}, - {CF_DIF, "CF_DIF"}, - {CF_TIFF, "CF_TIFF"}, - {CF_OEMTEXT, "CF_OEMTEXT"}, - {CF_DIB, "CF_DIB"}, - {CF_PENDATA, "CF_PENDATA"}, - {CF_RIFF, "CF_RIFF"}, - {CF_WAVE, "CF_WAVE"}, - {CF_UNICODETEXT, "CF_UNICODETEXT"}, - {CF_DIBV5, "CF_DIBV5"}, - {CF_OWNERDISPLAY, "CF_OWNERDISPLAY"}, - - // These formats are predefined but explicitly blocked from use, - // either due to passing along handles, or concerns regarding exposing - // private information. - // {CF_MAX, "CF_MAX"}, - // {CF_PRIVATEFIRST, "CF_PRIVATEFIRST"}, - // {CF_PRIVATELAST, "CF_PRIVATELAST"}, - // {CF_GDIOBJFIRST, "CF_GDIOBJFIRST"}, - // {CF_GDIOBJLAST, "CF_GDIOBJLAST"}, - // {CF_BITMAP, "CF_BITMAP"}, - // {CF_SYLK, "CF_SYLK"}, - // {CF_METAFILEPICT, "CF_METAFILEPICT"}, - // {CF_DSPTEXT, "CF_DSPTEXT"}, - // {CF_DSPBITMAP, "CF_DSPBITMAP"}, - // {CF_DSPMETAFILEPICT, "CF_DSPMETAFILEPICT"}, - // {CF_DSPENHMETAFILE, "CF_DSPENHMETAFILE"}, - // {CF_ENHMETAFILE, "CF_ENHMETAFILE"}, - // {CF_HDROP, "CF_HDROP"}, - // {CF_LOCALE, "CF_LOCALE"}, - // {CF_PALETTE, "CF_PALETTE"}, - - }); - return *format_to_name; -} - -const base::flat_map<std::string, UINT>& PredefinedNameToFormatMap() { - // Use lambda constructor for thread-safe initialization of name_to_format. - static const base::NoDestructor<base::flat_map<std::string, UINT>> - name_to_format([] { - base::flat_map<std::string, UINT> new_name_to_format; - const auto& format_to_name = PredefinedFormatToNameMap(); - new_name_to_format.reserve(format_to_name.size()); - for (const auto& it : format_to_name) - new_name_to_format.emplace(it.second, it.first); - return new_name_to_format; - }()); - return *name_to_format; -} - -} // namespace +#include "ui/base/clipboard/clipboard_constants.h" namespace ui { // ClipboardFormatType implementation. +// Windows formats are backed by "Clipboard Formats", documented here: +// https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats ClipboardFormatType::ClipboardFormatType() = default; ClipboardFormatType::ClipboardFormatType(UINT native_format) @@ -91,17 +36,7 @@ ClipboardFormatType::ClipboardFormatType(UINT native_format, DWORD tymed) : data_{/* .cfFormat */ static_cast<CLIPFORMAT>(native_format), /* .ptd */ nullptr, /* .dwAspect */ DVASPECT_CONTENT, - /* .lindex */ index, /* .tymed*/ tymed} { - // Log the frequency of invalid formats being input into the constructor. - if (!native_format) { - static int error_count = 0; - ++error_count; - // TODO(https://crbug.com/1000919): Evaluate and remove UMA metrics after - // enough data is gathered. - base::UmaHistogramCounts100("Clipboard.RegisterClipboardFormatFailure", - error_count); - } -} + /* .lindex */ index, /* .tymed*/ tymed} {} ClipboardFormatType::~ClipboardFormatType() = default; @@ -122,22 +57,59 @@ ClipboardFormatType ClipboardFormatType::Deserialize( return ClipboardFormatType(clipboard_format); } +// static +std::string ClipboardFormatType::WebCustomFormatName(int index) { + return base::StrCat({"Web Custom Format", base::NumberToString(index)}); +} + +// static +std::string ClipboardFormatType::WebCustomFormatMapName() { + return "Web Custom Format Map"; +} + +// 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())); +} + +// static +const ClipboardFormatType& ClipboardFormatType::WebCustomFormatMap() { + static base::NoDestructor<ClipboardFormatType> format( + ::RegisterClipboardFormat( + base::ASCIIToWide(ClipboardFormatType::WebCustomFormatMapName()) + .c_str())); + return *format; +} + std::string ClipboardFormatType::GetName() const { - const auto& predefined_format_to_name = PredefinedFormatToNameMap(); - const auto it = predefined_format_to_name.find(data_.cfFormat); - if (it != predefined_format_to_name.end()) - return it->second; - - 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); - if (!name_size) { - // Input format doesn't exist or is predefined. - return std::string(); + if (ClipboardFormatType::PlainTextAType().ToFormatEtc().cfFormat == + data_.cfFormat) { + return kMimeTypeText; } - - return base::WideToUTF8(std::wstring(name_buffer->data(), name_size)); + if (ClipboardFormatType::HtmlType().ToFormatEtc().cfFormat == + data_.cfFormat) { + return kMimeTypeHTML; + } + if (ClipboardFormatType::RtfType().ToFormatEtc().cfFormat == data_.cfFormat) { + return kMimeTypeRTF; + } + if (CF_DIB == data_.cfFormat) + return kMimeTypePNG; + if (ClipboardFormatType::CFHDropType().ToFormatEtc().cfFormat == + data_.cfFormat || + ClipboardFormatType::FilenameType().ToFormatEtc().cfFormat == + data_.cfFormat || + ClipboardFormatType::FilenameAType().ToFormatEtc().cfFormat == + data_.cfFormat) { + return kMimeTypeURIList; + } + // Not a standard format type. + return std::string(); } bool ClipboardFormatType::operator<(const ClipboardFormatType& other) const { @@ -153,11 +125,6 @@ bool ClipboardFormatType::operator==(const ClipboardFormatType& other) const { // static ClipboardFormatType ClipboardFormatType::GetType( const std::string& format_string) { - const auto& predefined_name_to_format = PredefinedNameToFormatMap(); - const auto it = predefined_name_to_format.find(format_string); - if (it != predefined_name_to_format.end()) - return ClipboardFormatType(it->second); - return ClipboardFormatType( ::RegisterClipboardFormat(base::ASCIIToWide(format_string).c_str())); } @@ -169,19 +136,19 @@ ClipboardFormatType ClipboardFormatType::GetType( // ClipboardFormatTypes thread-safe on all platforms. // static -const ClipboardFormatType& ClipboardFormatType::GetFilenamesType() { - return GetFilenameType(); +const ClipboardFormatType& ClipboardFormatType::FilenamesType() { + return FilenameType(); } // static -const ClipboardFormatType& ClipboardFormatType::GetUrlType() { +const ClipboardFormatType& ClipboardFormatType::UrlType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_INETURLW)); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetPlainTextType() { +const ClipboardFormatType& ClipboardFormatType::PlainTextType() { static base::NoDestructor<ClipboardFormatType> format(CF_UNICODETEXT); return *format; } @@ -189,14 +156,14 @@ const ClipboardFormatType& ClipboardFormatType::GetPlainTextType() { // MS HTML Format // static -const ClipboardFormatType& ClipboardFormatType::GetHtmlType() { +const ClipboardFormatType& ClipboardFormatType::HtmlType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(L"HTML Format")); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetSvgType() { +const ClipboardFormatType& ClipboardFormatType::SvgType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_MIME_SVG_XML)); return *format; @@ -205,40 +172,40 @@ const ClipboardFormatType& ClipboardFormatType::GetSvgType() { // MS RTF Format // static -const ClipboardFormatType& ClipboardFormatType::GetRtfType() { +const ClipboardFormatType& ClipboardFormatType::RtfType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(L"Rich Text Format")); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetPngType() { +const ClipboardFormatType& ClipboardFormatType::PngType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(L"PNG")); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetBitmapType() { +const ClipboardFormatType& ClipboardFormatType::BitmapType() { static base::NoDestructor<ClipboardFormatType> format(CF_DIBV5); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetUrlAType() { +const ClipboardFormatType& ClipboardFormatType::UrlAType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_INETURLA)); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetPlainTextAType() { +const ClipboardFormatType& ClipboardFormatType::PlainTextAType() { static base::NoDestructor<ClipboardFormatType> format(CF_TEXT); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetFilenameAType() { +const ClipboardFormatType& ClipboardFormatType::FilenameAType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_FILENAMEA)); return *format; @@ -246,14 +213,14 @@ const ClipboardFormatType& ClipboardFormatType::GetFilenameAType() { // Firefox text/html // static -const ClipboardFormatType& ClipboardFormatType::GetTextHtmlType() { +const ClipboardFormatType& ClipboardFormatType::TextHtmlType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(L"text/html")); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetCFHDropType() { +const ClipboardFormatType& ClipboardFormatType::CFHDropType() { static base::NoDestructor<ClipboardFormatType> format(CF_HDROP); return *format; } @@ -262,24 +229,24 @@ const ClipboardFormatType& ClipboardFormatType::GetCFHDropType() { // ANSI format (e.g., it could be that it doesn't support Unicode). So need to // register both the ANSI and Unicode file group descriptors. // static -const ClipboardFormatType& ClipboardFormatType::GetFileDescriptorAType() { +const ClipboardFormatType& ClipboardFormatType::FileDescriptorAType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA)); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetFileDescriptorType() { +const ClipboardFormatType& ClipboardFormatType::FileDescriptorType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW)); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetFileContentZeroType() { +const ClipboardFormatType& ClipboardFormatType::FileContentZeroType() { // This uses a storage media type of TYMED_HGLOBAL, which is not commonly // used with CFSTR_FILECONTENTS (but used in Chromium--see - // OSExchangeDataProviderWin::SetFileContents). Use GetFileContentAtIndexType + // OSExchangeDataProviderWin::SetFileContents). Use FileContentAtIndexType // if TYMED_ISTREAM and TYMED_ISTORAGE are needed. // TODO(https://crbug.com/950756): Should TYMED_ISTREAM / TYMED_ISTORAGE be // used instead of TYMED_HGLOBAL in @@ -292,17 +259,16 @@ const ClipboardFormatType& ClipboardFormatType::GetFileContentZeroType() { } // static -std::map<LONG, ClipboardFormatType>& -ClipboardFormatType::GetFileContentTypeMap() { +std::map<LONG, ClipboardFormatType>& ClipboardFormatType::FileContentTypeMap() { static base::NoDestructor<std::map<LONG, ClipboardFormatType>> index_to_type_map; return *index_to_type_map; } // static -const ClipboardFormatType& ClipboardFormatType::GetFileContentAtIndexType( +const ClipboardFormatType& ClipboardFormatType::FileContentAtIndexType( LONG index) { - auto& index_to_type_map = GetFileContentTypeMap(); + auto& index_to_type_map = FileContentTypeMap(); auto insert_or_assign_result = index_to_type_map.insert( {index, @@ -312,35 +278,35 @@ const ClipboardFormatType& ClipboardFormatType::GetFileContentAtIndexType( } // static -const ClipboardFormatType& ClipboardFormatType::GetFilenameType() { +const ClipboardFormatType& ClipboardFormatType::FilenameType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_FILENAMEW)); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetIDListType() { +const ClipboardFormatType& ClipboardFormatType::IDListType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_SHELLIDLIST)); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetMozUrlType() { +const ClipboardFormatType& ClipboardFormatType::MozUrlType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(L"text/x-moz-url")); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetWebKitSmartPasteType() { +const ClipboardFormatType& ClipboardFormatType::WebKitSmartPasteType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(L"WebKit Smart Paste Format")); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetWebCustomDataType() { +const ClipboardFormatType& ClipboardFormatType::WebCustomDataType() { // TODO(http://crbug.com/106449): Standardize this name. static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(L"Chromium Web Custom MIME Data Format")); diff --git a/chromium/ui/base/clipboard/clipboard_mac.h b/chromium/ui/base/clipboard/clipboard_mac.h index f45d165368a..45d9d4b4e11 100644 --- a/chromium/ui/base/clipboard/clipboard_mac.h +++ b/chromium/ui/base/clipboard/clipboard_mac.h @@ -10,19 +10,31 @@ #include "base/component_export.h" #include "base/gtest_prod_util.h" -#include "base/macros.h" +#include "base/mac/foundation_util.h" #include "ui/base/clipboard/clipboard.h" @class NSPasteboard; namespace ui { +// Documentation on the underlying MacOS API this ultimately abstracts is +// available at https://developer.apple.com/documentation/appkit/nspasteboard +// and +// https://developer.apple.com/library/archive/documentation/General/Conceptual/Devpedia-CocoaApp/Pasteboard.html. class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardMac : public Clipboard { + public: + ClipboardMac(const ClipboardMac&) = delete; + ClipboardMac& operator=(const ClipboardMac&) = delete; + private: - FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageRetina); - FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageNonRetina); - FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, EmptyImage); - FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, PDFImage); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageRetina_Bitmap); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageNonRetina_Bitmap); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, EmptyImage_Bitmap); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, PDFImage_Bitmap); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageRetina_Png); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageNonRetina_Png); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, EmptyImage_Png); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, PDFImage_Png); friend class Clipboard; ClipboardMac(); @@ -31,7 +43,8 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardMac : public Clipboard { // Clipboard overrides: void OnPreShutdown() override; DataTransferEndpoint* GetSource(ClipboardBuffer buffer) const override; - uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override; + const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const override; bool IsFormatAvailable(const ClipboardFormatType& format, ClipboardBuffer buffer, const DataTransferEndpoint* data_dst) const override; @@ -81,12 +94,9 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardMac : public Clipboard { void ReadData(const ClipboardFormatType& format, const DataTransferEndpoint* data_dst, std::string* result) const override; - void WritePortableRepresentations( + void WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) override; - void WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) override; void WriteText(const char* text_data, size_t text_len) override; @@ -107,10 +117,16 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardMac : public Clipboard { const char* data_data, size_t data_len) override; + std::vector<uint8_t> ReadPngInternal(ClipboardBuffer buffer, + NSPasteboard* pasteboard) const; SkBitmap ReadImageInternal(ClipboardBuffer buffer, NSPasteboard* pasteboard) const; - DISALLOW_COPY_AND_ASSIGN(ClipboardMac); + // Mapping of OS-provided sequence number to a unique token. + mutable struct { + NSInteger sequence_number; + ClipboardSequenceNumberToken token; + } clipboard_sequence_; }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_mac.mm b/chromium/ui/base/clipboard/clipboard_mac.mm index 76b08ef78e5..0419eb5beb8 100644 --- a/chromium/ui/base/clipboard/clipboard_mac.mm +++ b/chromium/ui/base/clipboard/clipboard_mac.mm @@ -6,19 +6,19 @@ #import <Cocoa/Cocoa.h> #include <stdint.h> +#include "ui/base/clipboard/clipboard.h" #include <limits> -#include "base/feature_list.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/mac/foundation_util.h" #include "base/mac/mac_util.h" #include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_nsobject.h" +#include "base/memory/ref_counted_memory.h" #include "base/no_destructor.h" #include "base/notreached.h" -#include "base/stl_util.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" @@ -29,13 +29,15 @@ #import "third_party/mozilla/NSPasteboard+Utils.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/clipboard/clipboard_constants.h" +#include "ui/base/clipboard/clipboard_format_type.h" #include "ui/base/clipboard/clipboard_metrics.h" #include "ui/base/clipboard/clipboard_util_mac.h" #include "ui/base/clipboard/custom_data_helper.h" #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" -#include "ui/base/ui_base_features.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/codec/png_codec.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/image/image.h" #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" #include "url/gurl.h" @@ -50,6 +52,23 @@ NSPasteboard* GetPasteboard() { return pasteboard; } +base::scoped_nsobject<NSImage> GetNSImage(NSPasteboard* pasteboard) { + // If the pasteboard's image data is not to its liking, the guts of NSImage + // may throw, and that exception will leak. Prevent a crash in that case; + // a blank image is better. + base::scoped_nsobject<NSImage> image; + @try { + if (pasteboard) + image.reset([[NSImage alloc] initWithPasteboard:pasteboard]); + } @catch (id exception) { + } + if (!image) + return base::scoped_nsobject<NSImage>(); + if ([[image representations] count] == 0u) + return base::scoped_nsobject<NSImage>(); + return image; +} + } // namespace // Clipboard factory method. @@ -76,11 +95,17 @@ DataTransferEndpoint* ClipboardMac::GetSource(ClipboardBuffer buffer) const { return nullptr; } -uint64_t ClipboardMac::GetSequenceNumber(ClipboardBuffer buffer) const { +const ClipboardSequenceNumberToken& ClipboardMac::GetSequenceNumber( + ClipboardBuffer buffer) const { DCHECK(CalledOnValidThread()); DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - return [GetPasteboard() changeCount]; + NSInteger sequence_number = [GetPasteboard() changeCount]; + if (sequence_number != clipboard_sequence_.sequence_number) { + // Generate a unique token associated with the current sequence number. + clipboard_sequence_ = {sequence_number, ClipboardSequenceNumberToken()}; + } + return clipboard_sequence_.token; } // |data_dst| is not used. It's only passed to be consistent with other @@ -92,21 +117,21 @@ bool ClipboardMac::IsFormatAvailable( DCHECK(CalledOnValidThread()); DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - // Only support filenames if chrome://flags#clipboard-filenames is enabled. - if (format == ClipboardFormatType::GetFilenamesType() && - !base::FeatureList::IsEnabled(features::kClipboardFilenames)) { - return false; - } - // https://crbug.com/1016740#c21 base::scoped_nsobject<NSArray> types([[GetPasteboard() types] retain]); // Safari only places RTF on the pasteboard, never HTML. We can convert RTF // to HTML, so the presence of either indicates success when looking for HTML. - if ([format.ToNSString() isEqualToString:NSHTMLPboardType]) { + if (format == ClipboardFormatType::HtmlType()) { return [types containsObject:NSHTMLPboardType] || [types containsObject:NSRTFPboardType]; } + // Chrome can retrieve an image from the clipboard as either a bitmap or PNG. + if (format == ClipboardFormatType::PngType() || + format == ClipboardFormatType::BitmapType()) { + return [types containsObject:NSPasteboardTypePNG] || + [types containsObject:NSTIFFPboardType]; + } return [types containsObject:format.ToNSString()]; } @@ -146,16 +171,15 @@ void ClipboardMac::ReadAvailableTypes( NSPasteboard* pb = GetPasteboard(); types->clear(); - if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer, - data_dst)) + if (IsFormatAvailable(ClipboardFormatType::PlainTextType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeText)); - if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst)) + if (IsFormatAvailable(ClipboardFormatType::HtmlType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeHTML)); - if (IsFormatAvailable(ClipboardFormatType::GetSvgType(), buffer, data_dst)) + if (IsFormatAvailable(ClipboardFormatType::SvgType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeSvg)); - if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst)) + if (IsFormatAvailable(ClipboardFormatType::RtfType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeRTF)); - if (IsFormatAvailable(ClipboardFormatType::GetFilenamesType(), buffer, + if (IsFormatAvailable(ClipboardFormatType::FilenamesType(), buffer, data_dst)) { types->push_back(base::UTF8ToUTF16(kMimeTypeURIList)); } else if (pb && [NSImage canInitWithPasteboard:pb]) { @@ -275,7 +299,7 @@ void ClipboardMac::ReadRTF(ClipboardBuffer buffer, DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); RecordRead(ClipboardFormatMetric::kRtf); - return ReadData(ClipboardFormatType::GetRtfType(), data_dst, result); + return ReadData(ClipboardFormatType::RtfType(), data_dst, result); } // |data_dst| is not used. It's only passed to be consistent with other @@ -283,8 +307,8 @@ void ClipboardMac::ReadRTF(ClipboardBuffer buffer, void ClipboardMac::ReadPng(ClipboardBuffer buffer, const DataTransferEndpoint* data_dst, ReadPngCallback callback) const { - // TODO(crbug.com/1201018): Implement this. - NOTIMPLEMENTED(); + RecordRead(ClipboardFormatMetric::kPng); + std::move(callback).Run(ReadPngInternal(buffer, GetPasteboard())); } // |data_dst| is not used. It's only passed to be consistent with other @@ -367,23 +391,9 @@ void ClipboardMac::ReadData(const ClipboardFormatType& format, // |data_src| is not used. It's only passed to be consistent with other // platforms. -void ClipboardMac::WritePortableRepresentations( +void ClipboardMac::WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - - [GetPasteboard() declareTypes:@[] owner:nil]; - - for (const auto& object : objects) - DispatchPortableRepresentation(object.first, object.second); -} - -// |data_src| is not used. It's only passed to be consistent with other -// platforms. -void ClipboardMac::WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) { DCHECK(CalledOnValidThread()); @@ -392,6 +402,8 @@ void ClipboardMac::WritePlatformRepresentations( [GetPasteboard() declareTypes:@[] owner:nil]; DispatchPlatformRepresentations(std::move(platform_representations)); + for (const auto& object : objects) + DispatchPortableRepresentation(object.first, object.second); } void ClipboardMac::WriteText(const char* text_data, size_t text_len) { @@ -420,7 +432,7 @@ void ClipboardMac::WriteSvg(const char* markup_data, size_t markup_len) { } void ClipboardMac::WriteRTF(const char* rtf_data, size_t data_len) { - WriteData(ClipboardFormatType::GetRtfType(), rtf_data, data_len); + WriteData(ClipboardFormatType::RtfType(), rtf_data, data_len); } void ClipboardMac::WriteFilenames(std::vector<ui::FileInfo> filenames) { @@ -458,15 +470,7 @@ void ClipboardMac::WriteBitmap(const SkBitmap& bitmap) { NOTREACHED() << "SkBitmapToNSImageWithColorSpace failed"; return; } - // TODO (https://crbug.com/971916): Write NSImage directly to clipboard. - // An API to ask the NSImage to write itself to the clipboard comes in 10.6 :( - // For now, spit out the image as a TIFF. - NSData* tiff_data = [image TIFFRepresentation]; - LOG_IF(ERROR, tiff_data == nullptr) - << "Failed to allocate image for clipboard"; - if (tiff_data) { - [GetPasteboard() setData:tiff_data forType:NSTIFFPboardType]; - } + [GetPasteboard() writeObjects:@[ image ]]; } void ClipboardMac::WriteData(const ClipboardFormatType& format, @@ -479,43 +483,33 @@ void ClipboardMac::WriteData(const ClipboardFormatType& format, // Write an extra flavor that signifies WebKit was the last to modify the // pasteboard. This flavor has no data. void ClipboardMac::WriteWebSmartPaste() { - NSString* format = - ClipboardFormatType::GetWebKitSmartPasteType().ToNSString(); + NSString* format = ClipboardFormatType::WebKitSmartPasteType().ToNSString(); [GetPasteboard() setData:nil forType:format]; } +std::vector<uint8_t> ClipboardMac::ReadPngInternal( + ClipboardBuffer buffer, + NSPasteboard* pasteboard) const { + DCHECK(CalledOnValidThread()); + DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); + + base::scoped_nsobject<NSImage> image = GetNSImage(pasteboard); + if (!image) + return std::vector<uint8_t>(); + + scoped_refptr<base::RefCountedMemory> mem = gfx::Image(image).As1xPNGBytes(); + std::vector<uint8_t> image_data(mem->data(), mem->data() + mem->size()); + return image_data; +} + SkBitmap ClipboardMac::ReadImageInternal(ClipboardBuffer buffer, NSPasteboard* pasteboard) const { DCHECK(CalledOnValidThread()); DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - // If the pasteboard's image data is not to its liking, the guts of NSImage - // may throw, and that exception will leak. Prevent a crash in that case; - // a blank image is better. - base::scoped_nsobject<NSImage> image; - @try { - // TODO(crbug.com/1175483): remove first branch of this code when - // ClipboardFilenames feature flag is removed. - if ([[pasteboard types] containsObject:NSFilenamesPboardType]) { - // -[NSImage initWithPasteboard:] gets confused with copies of a single - // file from the Finder, so extract the path ourselves. - // http://crbug.com/553686 - NSArray* paths = [pasteboard propertyListForType:NSFilenamesPboardType]; - if ([paths count]) { - // If N number of files are selected from finder, choose the last one. - image.reset([[NSImage alloc] - initWithContentsOfURL:[NSURL fileURLWithPath:[paths lastObject]]]); - } - } else { - if (pasteboard) - image.reset([[NSImage alloc] initWithPasteboard:pasteboard]); - } - } @catch (id exception) { - } + base::scoped_nsobject<NSImage> image = GetNSImage(pasteboard); if (!image) return SkBitmap(); - if ([[image representations] count] == 0u) - return SkBitmap(); // This logic prevents loss of pixels from retina images, where size != pixel // size. In an ideal world, the concept of "retina-ness" would be plumbed all diff --git a/chromium/ui/base/clipboard/clipboard_mac_unittest.mm b/chromium/ui/base/clipboard/clipboard_mac_unittest.mm index 7e84fa95d1d..4b4886495d9 100644 --- a/chromium/ui/base/clipboard/clipboard_mac_unittest.mm +++ b/chromium/ui/base/clipboard/clipboard_mac_unittest.mm @@ -4,6 +4,8 @@ #import "ui/base/clipboard/clipboard_mac.h" +#include <vector> + #import <AppKit/AppKit.h> #include "base/mac/scoped_cftyperef.h" @@ -14,6 +16,7 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/clipboard/clipboard_buffer.h" #include "ui/base/clipboard/clipboard_util_mac.h" +#include "ui/gfx/codec/png_codec.h" @interface RedView : NSView @end @@ -69,7 +72,7 @@ class ClipboardMacTest : public PlatformTest { } }; -TEST_F(ClipboardMacTest, ReadImageRetina) { +TEST_F(ClipboardMacTest, ReadImageRetina_Bitmap) { int32_t width = 99; int32_t height = 101; scoped_refptr<UniquePasteboard> pasteboard = new UniquePasteboard; @@ -85,7 +88,25 @@ TEST_F(ClipboardMacTest, ReadImageRetina) { EXPECT_EQ(2 * height, bitmap.height()); } -TEST_F(ClipboardMacTest, ReadImageNonRetina) { +TEST_F(ClipboardMacTest, ReadImageRetina_Png) { + int32_t width = 99; + int32_t height = 101; + scoped_refptr<UniquePasteboard> pasteboard = new UniquePasteboard; + base::scoped_nsobject<NSImage> image = CreateImage(width, height, true); + [pasteboard->get() writeObjects:@[ image.get() ]]; + + Clipboard* clipboard = Clipboard::GetForCurrentThread(); + ClipboardMac* clipboard_mac = static_cast<ClipboardMac*>(clipboard); + + std::vector<uint8_t> png_data = clipboard_mac->ReadPngInternal( + ClipboardBuffer::kCopyPaste, pasteboard->get()); + SkBitmap bitmap; + gfx::PNGCodec::Decode(png_data.data(), png_data.size(), &bitmap); + EXPECT_EQ(2 * width, bitmap.width()); + EXPECT_EQ(2 * height, bitmap.height()); +} + +TEST_F(ClipboardMacTest, ReadImageNonRetina_Bitmap) { int32_t width = 99; int32_t height = 101; scoped_refptr<UniquePasteboard> pasteboard = new UniquePasteboard; @@ -101,7 +122,25 @@ TEST_F(ClipboardMacTest, ReadImageNonRetina) { EXPECT_EQ(height, bitmap.height()); } -TEST_F(ClipboardMacTest, EmptyImage) { +TEST_F(ClipboardMacTest, ReadImageNonRetina_Png) { + int32_t width = 99; + int32_t height = 101; + scoped_refptr<UniquePasteboard> pasteboard = new UniquePasteboard; + base::scoped_nsobject<NSImage> image = CreateImage(width, height, false); + [pasteboard->get() writeObjects:@[ image.get() ]]; + + Clipboard* clipboard = Clipboard::GetForCurrentThread(); + ClipboardMac* clipboard_mac = static_cast<ClipboardMac*>(clipboard); + + std::vector<uint8_t> png_data = clipboard_mac->ReadPngInternal( + ClipboardBuffer::kCopyPaste, pasteboard->get()); + SkBitmap bitmap; + gfx::PNGCodec::Decode(png_data.data(), png_data.size(), &bitmap); + EXPECT_EQ(width, bitmap.width()); + EXPECT_EQ(height, bitmap.height()); +} + +TEST_F(ClipboardMacTest, EmptyImage_Bitmap) { base::scoped_nsobject<NSImage> image([[NSImage alloc] init]); scoped_refptr<UniquePasteboard> pasteboard = new UniquePasteboard; [pasteboard->get() writeObjects:@[ image.get() ]]; @@ -115,7 +154,23 @@ TEST_F(ClipboardMacTest, EmptyImage) { EXPECT_EQ(0, bitmap.height()); } -TEST_F(ClipboardMacTest, PDFImage) { +TEST_F(ClipboardMacTest, EmptyImage_Png) { + base::scoped_nsobject<NSImage> image([[NSImage alloc] init]); + scoped_refptr<UniquePasteboard> pasteboard = new UniquePasteboard; + [pasteboard->get() writeObjects:@[ image.get() ]]; + + Clipboard* clipboard = Clipboard::GetForCurrentThread(); + ClipboardMac* clipboard_mac = static_cast<ClipboardMac*>(clipboard); + + std::vector<uint8_t> png_data = clipboard_mac->ReadPngInternal( + ClipboardBuffer::kCopyPaste, pasteboard->get()); + SkBitmap bitmap; + gfx::PNGCodec::Decode(png_data.data(), png_data.size(), &bitmap); + EXPECT_EQ(0, bitmap.width()); + EXPECT_EQ(0, bitmap.height()); +} + +TEST_F(ClipboardMacTest, PDFImage_Bitmap) { int32_t width = 99; int32_t height = 101; NSRect frame = NSMakeRect(0, 0, width, height); @@ -138,4 +193,29 @@ TEST_F(ClipboardMacTest, PDFImage) { EXPECT_EQ(height, bitmap.height()); } +TEST_F(ClipboardMacTest, PDFImage_Png) { + int32_t width = 99; + int32_t height = 101; + NSRect frame = NSMakeRect(0, 0, width, height); + + // This seems like a round-about way of getting a NSPDFImageRep to shove into + // an NSPasteboard. However, I haven't found any other way of generating a + // "PDF" image that makes NSPasteboard happy. + base::scoped_nsobject<NSView> v([[RedView alloc] initWithFrame:frame]); + NSData* data = [v dataWithPDFInsideRect:frame]; + + scoped_refptr<UniquePasteboard> pasteboard = new UniquePasteboard; + [pasteboard->get() setData:data forType:NSPasteboardTypePDF]; + + Clipboard* clipboard = Clipboard::GetForCurrentThread(); + ClipboardMac* clipboard_mac = static_cast<ClipboardMac*>(clipboard); + + std::vector<uint8_t> png_data = clipboard_mac->ReadPngInternal( + ClipboardBuffer::kCopyPaste, pasteboard->get()); + SkBitmap bitmap; + gfx::PNGCodec::Decode(png_data.data(), png_data.size(), &bitmap); + EXPECT_EQ(width, bitmap.width()); + EXPECT_EQ(height, bitmap.height()); +} + } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_monitor.h b/chromium/ui/base/clipboard/clipboard_monitor.h index 5ba97a41eaf..5a7ad27ea28 100644 --- a/chromium/ui/base/clipboard/clipboard_monitor.h +++ b/chromium/ui/base/clipboard/clipboard_monitor.h @@ -6,7 +6,6 @@ #define UI_BASE_CLIPBOARD_CLIPBOARD_MONITOR_H_ #include "base/component_export.h" -#include "base/macros.h" #include "base/no_destructor.h" #include "base/observer_list.h" #include "base/threading/thread_checker.h" @@ -23,6 +22,9 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardMonitor { public: static ClipboardMonitor* GetInstance(); + ClipboardMonitor(const ClipboardMonitor&) = delete; + ClipboardMonitor& operator=(const ClipboardMonitor&) = delete; + // Adds an observer. void AddObserver(ClipboardObserver* observer); @@ -46,8 +48,6 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardMonitor { base::ObserverList<ClipboardObserver>::Unchecked observers_; THREAD_CHECKER(thread_checker_); - - DISALLOW_COPY_AND_ASSIGN(ClipboardMonitor); }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_non_backed.cc b/chromium/ui/base/clipboard/clipboard_non_backed.cc index 3cacd71de81..89bdb1068bb 100644 --- a/chromium/ui/base/clipboard/clipboard_non_backed.cc +++ b/chromium/ui/base/clipboard/clipboard_non_backed.cc @@ -13,9 +13,8 @@ #include <utility> #include "base/check_op.h" -#include "base/feature_list.h" +#include "base/containers/contains.h" #include "base/files/file_path.h" -#include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/no_destructor.h" #include "base/notreached.h" @@ -32,7 +31,6 @@ #include "ui/base/clipboard/custom_data_helper.h" #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" #include "ui/base/data_transfer_policy/data_transfer_policy_controller.h" -#include "ui/base/ui_base_features.h" #include "ui/gfx/geometry/size.h" namespace ui { @@ -88,16 +86,19 @@ bool IsRegisteredInstance(const Clipboard* clipboard) { class ClipboardInternal { public: ClipboardInternal() = default; - + ClipboardInternal(const ClipboardInternal&) = delete; + ClipboardInternal& operator=(const ClipboardInternal&) = delete; ~ClipboardInternal() = default; void Clear() { - ++sequence_number_; + sequence_number_ = ClipboardSequenceNumberToken(); data_.reset(); ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged(); } - uint64_t sequence_number() const { return sequence_number_; } + const ClipboardSequenceNumberToken& sequence_number() const { + return sequence_number_; + } // Returns the current clipboard data, which may be nullptr if nothing has // been written since the last Clear(). @@ -180,6 +181,14 @@ class ClipboardInternal { *result = data->rtf_data(); } + // Reads png from the ClipboardData. + std::vector<uint8_t> ReadPng() const { + if (!HasFormat(ClipboardInternalFormat::kPng)) + return std::vector<uint8_t>(); + + return GetData()->png(); + } + // Reads image from the ClipboardData. SkBitmap ReadImage() const { SkBitmap img; @@ -243,7 +252,7 @@ class ClipboardInternal { DCHECK(data); std::unique_ptr<ClipboardData> previous_data = std::move(data_); data_ = std::move(data); - ++sequence_number_; + sequence_number_ = ClipboardSequenceNumberToken(); ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged(); return previous_data; } @@ -254,7 +263,8 @@ class ClipboardInternal { auto* data = GetData(); if (!policy_controller || !data) return true; - return policy_controller->IsClipboardReadAllowed(data->source(), data_dst); + return policy_controller->IsClipboardReadAllowed(data->source(), data_dst, + data->size()); } private: @@ -268,9 +278,7 @@ class ClipboardInternal { std::unique_ptr<ClipboardData> data_; // Sequence number uniquely identifying clipboard state. - uint64_t sequence_number_ = 0; - - DISALLOW_COPY_AND_ASSIGN(ClipboardInternal); + ClipboardSequenceNumberToken sequence_number_; }; // Helper class to build a ClipboardData object and write it to clipboard. @@ -407,7 +415,8 @@ DataTransferEndpoint* ClipboardNonBacked::GetSource( return data ? data->source() : nullptr; } -uint64_t ClipboardNonBacked::GetSequenceNumber(ClipboardBuffer buffer) const { +const ClipboardSequenceNumberToken& ClipboardNonBacked::GetSequenceNumber( + ClipboardBuffer buffer) const { DCHECK(CalledOnValidThread()); return clipboard_internal_->sequence_number(); } @@ -422,29 +431,27 @@ bool ClipboardNonBacked::IsFormatAvailable( if (!clipboard_internal_->IsReadAllowed(data_dst)) return false; - if (format == ClipboardFormatType::GetPlainTextType() || - format == ClipboardFormatType::GetUrlType()) + if (format == ClipboardFormatType::PlainTextType() || + format == ClipboardFormatType::UrlType()) return clipboard_internal_->IsFormatAvailable( ClipboardInternalFormat::kText); - if (format == ClipboardFormatType::GetHtmlType()) + if (format == ClipboardFormatType::HtmlType()) return clipboard_internal_->IsFormatAvailable( ClipboardInternalFormat::kHtml); - if (format == ClipboardFormatType::GetSvgType()) + if (format == ClipboardFormatType::SvgType()) return clipboard_internal_->IsFormatAvailable( ClipboardInternalFormat::kSvg); - if (format == ClipboardFormatType::GetRtfType()) + if (format == ClipboardFormatType::RtfType()) return clipboard_internal_->IsFormatAvailable( ClipboardInternalFormat::kRtf); - if (format == ClipboardFormatType::GetPngType() || - format == ClipboardFormatType::GetBitmapType()) + if (format == ClipboardFormatType::PngType() || + format == ClipboardFormatType::BitmapType()) return clipboard_internal_->IsFormatAvailable( ClipboardInternalFormat::kPng); - if (format == ClipboardFormatType::GetWebKitSmartPasteType()) + if (format == ClipboardFormatType::WebKitSmartPasteType()) return clipboard_internal_->IsFormatAvailable( ClipboardInternalFormat::kWeb); - // Only support filenames if chrome://flags#clipboard-filenames is enabled. - if (format == ClipboardFormatType::GetFilenamesType() && - base::FeatureList::IsEnabled(features::kClipboardFilenames)) + if (format == ClipboardFormatType::FilenamesType()) return clipboard_internal_->IsFormatAvailable( ClipboardInternalFormat::kFilenames); const ClipboardData* data = clipboard_internal_->GetData(); @@ -468,20 +475,18 @@ void ClipboardNonBacked::ReadAvailableTypes( return; types->clear(); - if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer, - data_dst)) + if (IsFormatAvailable(ClipboardFormatType::PlainTextType(), buffer, data_dst)) types->push_back( - base::UTF8ToUTF16(ClipboardFormatType::GetPlainTextType().GetName())); - if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst)) + base::UTF8ToUTF16(ClipboardFormatType::PlainTextType().GetName())); + if (IsFormatAvailable(ClipboardFormatType::HtmlType(), buffer, data_dst)) types->push_back( - base::UTF8ToUTF16(ClipboardFormatType::GetHtmlType().GetName())); - if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst)) + base::UTF8ToUTF16(ClipboardFormatType::HtmlType().GetName())); + if (IsFormatAvailable(ClipboardFormatType::RtfType(), buffer, data_dst)) types->push_back( - base::UTF8ToUTF16(ClipboardFormatType::GetRtfType().GetName())); - if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer, data_dst)) + base::UTF8ToUTF16(ClipboardFormatType::RtfType().GetName())); + if (IsFormatAvailable(ClipboardFormatType::BitmapType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypePNG)); - if (IsFormatAvailable(ClipboardFormatType::GetFilenamesType(), buffer, - data_dst)) + if (IsFormatAvailable(ClipboardFormatType::FilenamesType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeURIList)); if (clipboard_internal_->IsFormatAvailable( @@ -505,21 +510,20 @@ ClipboardNonBacked::ReadAvailablePlatformSpecificFormatNames( return types; // Includes all non-pickled AvailableTypes. - if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer, + if (IsFormatAvailable(ClipboardFormatType::PlainTextType(), buffer, data_dst)) { types.push_back( - base::UTF8ToUTF16(ClipboardFormatType::GetPlainTextType().GetName())); + base::UTF8ToUTF16(ClipboardFormatType::PlainTextType().GetName())); } - if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst)) { + if (IsFormatAvailable(ClipboardFormatType::HtmlType(), buffer, data_dst)) { types.push_back( - base::UTF8ToUTF16(ClipboardFormatType::GetHtmlType().GetName())); + base::UTF8ToUTF16(ClipboardFormatType::HtmlType().GetName())); } - if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst)) { + if (IsFormatAvailable(ClipboardFormatType::RtfType(), buffer, data_dst)) { types.push_back( - base::UTF8ToUTF16(ClipboardFormatType::GetRtfType().GetName())); + base::UTF8ToUTF16(ClipboardFormatType::RtfType().GetName())); } - if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer, - data_dst)) { + if (IsFormatAvailable(ClipboardFormatType::BitmapType(), buffer, data_dst)) { types.push_back(base::UTF8ToUTF16(kMimeTypePNG)); } @@ -609,8 +613,18 @@ void ClipboardNonBacked::ReadPng(ClipboardBuffer buffer, const DataTransferEndpoint* data_dst, ReadPngCallback callback) const { DCHECK(CalledOnValidThread()); - // TODO(crbug.com/1201018): Implement this. - NOTIMPLEMENTED(); + + if (!clipboard_internal_->IsReadAllowed(data_dst)) { + std::move(callback).Run(std::vector<uint8_t>()); + return; + } + + RecordRead(ClipboardFormatMetric::kPng); + std::move(callback).Run(clipboard_internal_->ReadPng()); + +#if BUILDFLAG(IS_CHROMEOS_ASH) + ClipboardMonitor::GetInstance()->NotifyClipboardDataRead(); +#endif } void ClipboardNonBacked::ReadImage(ClipboardBuffer buffer, @@ -707,26 +721,17 @@ bool ClipboardNonBacked::IsSelectionBufferAvailable() const { } #endif // defined(USE_OZONE) -void ClipboardNonBacked::WritePortableRepresentations( +void ClipboardNonBacked::WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) { - DCHECK(CalledOnValidThread()); - DCHECK(IsSupportedClipboardBuffer(buffer)); - for (const auto& object : objects) - DispatchPortableRepresentation(object.first, object.second); - ClipboardDataBuilder::CommitToClipboard(clipboard_internal_.get(), - std::move(data_src)); -} - -void ClipboardNonBacked::WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) { DCHECK(CalledOnValidThread()); DCHECK(IsSupportedClipboardBuffer(buffer)); DispatchPlatformRepresentations(std::move(platform_representations)); + for (const auto& object : objects) + DispatchPortableRepresentation(object.first, object.second); ClipboardDataBuilder::CommitToClipboard(clipboard_internal_.get(), std::move(data_src)); diff --git a/chromium/ui/base/clipboard/clipboard_non_backed.h b/chromium/ui/base/clipboard/clipboard_non_backed.h index 17403e75391..7b67ca07b02 100644 --- a/chromium/ui/base/clipboard/clipboard_non_backed.h +++ b/chromium/ui/base/clipboard/clipboard_non_backed.h @@ -10,7 +10,6 @@ #include "base/component_export.h" #include "base/gtest_prod_util.h" -#include "base/macros.h" #include "ui/base/clipboard/clipboard.h" namespace ui { @@ -32,6 +31,9 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardNonBacked // the current thread is in fact an instance of ClipboardNonBacked. static ClipboardNonBacked* GetForCurrentThread(); + ClipboardNonBacked(const ClipboardNonBacked&) = delete; + ClipboardNonBacked& operator=(const ClipboardNonBacked&) = delete; + // Returns the current ClipboardData. const ClipboardData* GetClipboardData(DataTransferEndpoint* data_dst) const; @@ -42,7 +44,8 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardNonBacked // Clipboard overrides: DataTransferEndpoint* GetSource(ClipboardBuffer buffer) const override; - uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override; + const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const override; private: friend class Clipboard; @@ -103,12 +106,9 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardNonBacked #if defined(USE_OZONE) bool IsSelectionBufferAvailable() const override; #endif // defined(USE_OZONE) - void WritePortableRepresentations( + void WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) override; - void WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) override; void WriteText(const char* text_data, size_t text_len) override; @@ -130,8 +130,6 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardNonBacked size_t data_len) override; const std::unique_ptr<ClipboardInternal> clipboard_internal_; - - DISALLOW_COPY_AND_ASSIGN(ClipboardNonBacked); }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_non_backed_unittest.cc b/chromium/ui/base/clipboard/clipboard_non_backed_unittest.cc index 963dbc4297f..9cba3aa94d3 100644 --- a/chromium/ui/base/clipboard/clipboard_non_backed_unittest.cc +++ b/chromium/ui/base/clipboard/clipboard_non_backed_unittest.cc @@ -8,12 +8,12 @@ #include <string> #include <vector> +#include "base/pickle.h" #include "base/strings/utf_string_conversions.h" #include "base/test/metrics/histogram_tester.h" -#include "base/test/scoped_feature_list.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/clipboard/clipboard_data.h" -#include "ui/base/ui_base_features.h" +#include "ui/base/clipboard/custom_data_helper.h" namespace ui { namespace { @@ -92,9 +92,7 @@ TEST_F(ClipboardNonBackedTest, AdminWriteDoesNotRecordHistograms) { // Tests that site bookmark URLs are accessed as text, and // IsFormatAvailable('text/uri-list') is only true for files. TEST_F(ClipboardNonBackedTest, TextURIList) { - base::test::ScopedFeatureList features; - features.InitWithFeatures({features::kClipboardFilenames}, {}); - EXPECT_EQ("text/uri-list", ClipboardFormatType::GetFilenamesType().GetName()); + EXPECT_EQ("text/uri-list", ClipboardFormatType::FilenamesType().GetName()); auto data = std::make_unique<ClipboardData>(); data->set_bookmark_url("http://example.com"); @@ -103,27 +101,47 @@ TEST_F(ClipboardNonBackedTest, TextURIList) { clipboard()->ReadAvailableTypes(ClipboardBuffer::kCopyPaste, /*data_dst=*/nullptr, &types); - // With bookmark data, available types should be only 'text/plain'. + // Bookmark data uses mime type 'text/plain'. EXPECT_EQ(std::vector<std::string>({"text/plain"}), UTF8Types(types)); - EXPECT_TRUE(clipboard()->IsFormatAvailable(ClipboardFormatType::GetUrlType(), + EXPECT_TRUE(clipboard()->IsFormatAvailable(ClipboardFormatType::UrlType(), ClipboardBuffer::kCopyPaste, /*data_dst=*/nullptr)); EXPECT_FALSE(clipboard()->IsFormatAvailable( - ClipboardFormatType::GetFilenamesType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::FilenamesType(), ClipboardBuffer::kCopyPaste, /*data_dst=*/nullptr)); - // With filenames data, available types should be 'text/uri-list'. + // Filenames data uses mime type 'text/uri-list'. data = std::make_unique<ClipboardData>(); data->set_filenames({FileInfo(base::FilePath("/path"), base::FilePath())}); clipboard()->WriteClipboardData(std::move(data)); clipboard()->ReadAvailableTypes(ClipboardBuffer::kCopyPaste, /*data_dst=*/nullptr, &types); EXPECT_EQ(std::vector<std::string>({"text/uri-list"}), UTF8Types(types)); - EXPECT_FALSE(clipboard()->IsFormatAvailable(ClipboardFormatType::GetUrlType(), + EXPECT_FALSE(clipboard()->IsFormatAvailable(ClipboardFormatType::UrlType(), ClipboardBuffer::kCopyPaste, /*data_dst=*/nullptr)); EXPECT_TRUE(clipboard()->IsFormatAvailable( - ClipboardFormatType::GetFilenamesType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::FilenamesType(), ClipboardBuffer::kCopyPaste, + /*data_dst=*/nullptr)); + + // Filenames data uses mime type 'text/uri-list', but clients can also set + // 'text/uri-list' as one of the custom data types. When it is set as a custom + // type, it will be returned from ReadAvailableTypes(), but + // IsFormatAvailable(FilenamesType()) is false. + data = std::make_unique<ClipboardData>(); + base::flat_map<std::u16string, std::u16string> custom_data; + custom_data[u"text/uri-list"] = u"data"; + base::Pickle pickle; + ui::WriteCustomDataToPickle(custom_data, &pickle); + data->SetCustomData( + ui::ClipboardFormatType::WebCustomDataType().Serialize(), + std::string(static_cast<const char*>(pickle.data()), pickle.size())); + clipboard()->WriteClipboardData(std::move(data)); + clipboard()->ReadAvailableTypes(ClipboardBuffer::kCopyPaste, + /*data_dst=*/nullptr, &types); + EXPECT_EQ(std::vector<std::string>({"text/uri-list"}), UTF8Types(types)); + EXPECT_FALSE(clipboard()->IsFormatAvailable( + ClipboardFormatType::FilenamesType(), ClipboardBuffer::kCopyPaste, /*data_dst=*/nullptr)); } diff --git a/chromium/ui/base/clipboard/clipboard_ozone.cc b/chromium/ui/base/clipboard/clipboard_ozone.cc index 12e3371b4ee..62823f77885 100644 --- a/chromium/ui/base/clipboard/clipboard_ozone.cc +++ b/chromium/ui/base/clipboard/clipboard_ozone.cc @@ -13,7 +13,6 @@ #include "base/containers/contains.h" #include "base/containers/flat_map.h" #include "base/containers/span.h" -#include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/notreached.h" #include "base/run_loop.h" @@ -99,7 +98,8 @@ class ClipboardOzone::AsyncClipboardOzone { platform_clipboard_->SetClipboardDataChangedCallback( std::move(update_sequence_cb)); } - + AsyncClipboardOzone(const AsyncClipboardOzone&) = delete; + AsyncClipboardOzone& operator=(const AsyncClipboardOzone&) = delete; ~AsyncClipboardOzone() = default; bool IsSelectionBufferAvailable() const { @@ -164,7 +164,8 @@ class ClipboardOzone::AsyncClipboardOzone { } } - uint64_t GetSequenceNumber(ClipboardBuffer buffer) { + const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const { return buffer == ClipboardBuffer::kCopyPaste ? clipboard_sequence_number_ : selection_sequence_number_; } @@ -275,9 +276,9 @@ class ClipboardOzone::AsyncClipboardOzone { DCHECK(buffer == ClipboardBuffer::kCopyPaste || platform_clipboard_->IsSelectionBufferAvailable()); if (buffer == ClipboardBuffer::kCopyPaste) - clipboard_sequence_number_++; + clipboard_sequence_number_ = ClipboardSequenceNumberToken(); else - selection_sequence_number_++; + selection_sequence_number_ = ClipboardSequenceNumberToken(); ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged(); } @@ -296,12 +297,10 @@ class ClipboardOzone::AsyncClipboardOzone { // Provides communication to a system clipboard under ozone level. PlatformClipboard* const platform_clipboard_ = nullptr; - uint64_t clipboard_sequence_number_ = 0; - uint64_t selection_sequence_number_ = 0; + ClipboardSequenceNumberToken clipboard_sequence_number_; + ClipboardSequenceNumberToken selection_sequence_number_; base::WeakPtrFactory<AsyncClipboardOzone> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(AsyncClipboardOzone); }; // ClipboardOzone implementation. @@ -329,7 +328,8 @@ DataTransferEndpoint* ClipboardOzone::GetSource(ClipboardBuffer buffer) const { return it == data_src_.end() ? nullptr : it->second.get(); } -uint64_t ClipboardOzone::GetSequenceNumber(ClipboardBuffer buffer) const { +const ClipboardSequenceNumberToken& ClipboardOzone::GetSequenceNumber( + ClipboardBuffer buffer) const { return async_clipboard_ozone_->GetSequenceNumber(buffer); } @@ -364,9 +364,9 @@ void ClipboardOzone::ReadAvailableTypes( // Special handling for chromium/x-web-custom-data. // We must read the data and deserialize it to find the list // of mime types to report. - if (mime_type == ClipboardFormatType::GetWebCustomDataType().GetName()) { + if (mime_type == ClipboardFormatType::WebCustomDataType().GetName()) { auto data = async_clipboard_ozone_->ReadClipboardDataAndWait( - buffer, ClipboardFormatType::GetWebCustomDataType().GetName()); + buffer, ClipboardFormatType::WebCustomDataType().GetName()); ReadCustomDataTypes(data.data(), data.size(), types); } else { types->push_back(base::UTF8ToUTF16(mime_type)); @@ -468,8 +468,8 @@ void ClipboardOzone::ReadRTF(ClipboardBuffer buffer, void ClipboardOzone::ReadPng(ClipboardBuffer buffer, const DataTransferEndpoint* data_dst, ReadPngCallback callback) const { - // TODO(crbug.com/1201018): Implement this. - NOTIMPLEMENTED(); + RecordRead(ClipboardFormatMetric::kPng); + std::move(callback).Run(ReadPngInternal(buffer)); } // TODO(crbug.com/1103194): |data_dst| should be supported. @@ -477,7 +477,10 @@ void ClipboardOzone::ReadImage(ClipboardBuffer buffer, const DataTransferEndpoint* data_dst, ReadImageCallback callback) const { RecordRead(ClipboardFormatMetric::kImage); - std::move(callback).Run(ReadImageInternal(buffer)); + auto png_data = ReadPngInternal(buffer); + SkBitmap bitmap; + gfx::PNGCodec::Decode(png_data.data(), png_data.size(), &bitmap); + std::move(callback).Run(bitmap); } // TODO(crbug.com/1103194): |data_dst| should be supported. @@ -531,18 +534,8 @@ bool ClipboardOzone::IsSelectionBufferAvailable() const { return async_clipboard_ozone_->IsSelectionBufferAvailable(); } -// TODO(crbug.com/1103194): |data_src| should be supported -void ClipboardOzone::WritePortableRepresentations( - ClipboardBuffer buffer, - const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) { - DCHECK(CalledOnValidThread()); - - async_clipboard_ozone_->PrepareForWriting(); - for (const auto& object : objects) - DispatchPortableRepresentation(object.first, object.second); - async_clipboard_ozone_->OfferData(buffer); - +void ClipboardOzone::WritePortableTextRepresentation(ClipboardBuffer buffer, + const ObjectMap& objects) { // Just like Non-Backed/X11 implementation does, copy text data from the // copy/paste selection to the primary selection. if (buffer == ClipboardBuffer::kCopyPaste && IsSelectionBufferAvailable()) { @@ -555,21 +548,24 @@ void ClipboardOzone::WritePortableRepresentations( async_clipboard_ozone_->OfferData(ClipboardBuffer::kSelection); } } - - data_src_[buffer] = std::move(data_src); } // TODO(crbug.com/1103194): |data_src| should be supported -void ClipboardOzone::WritePlatformRepresentations( +void ClipboardOzone::WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, + const ObjectMap& objects, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) { DCHECK(CalledOnValidThread()); async_clipboard_ozone_->PrepareForWriting(); DispatchPlatformRepresentations(std::move(platform_representations)); + for (const auto& object : objects) + DispatchPortableRepresentation(object.first, object.second); async_clipboard_ozone_->OfferData(buffer); + WritePortableTextRepresentation(buffer, objects); + data_src_[buffer] = std::move(data_src); } @@ -637,16 +633,13 @@ void ClipboardOzone::WriteData(const ClipboardFormatType& format, async_clipboard_ozone_->InsertData(std::move(data), {format.GetName()}); } -SkBitmap ClipboardOzone::ReadImageInternal(ClipboardBuffer buffer) const { +std::vector<uint8_t> ClipboardOzone::ReadPngInternal( + ClipboardBuffer buffer) const { DCHECK(CalledOnValidThread()); - auto clipboard_data = + base::span<uint8_t> clipboard_data = async_clipboard_ozone_->ReadClipboardDataAndWait(buffer, kMimeTypePNG); - SkBitmap bitmap; - if (!gfx::PNGCodec::Decode(clipboard_data.data(), clipboard_data.size(), - &bitmap)) - return {}; - return SkBitmap(bitmap); + return std::vector<uint8_t>(clipboard_data.begin(), clipboard_data.end()); } } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_ozone.h b/chromium/ui/base/clipboard/clipboard_ozone.h index 730cf5d318c..a4b1f2a2788 100644 --- a/chromium/ui/base/clipboard/clipboard_ozone.h +++ b/chromium/ui/base/clipboard/clipboard_ozone.h @@ -9,7 +9,6 @@ #include <string> #include <vector> -#include "base/macros.h" #include "ui/base/clipboard/clipboard.h" namespace ui { @@ -17,6 +16,10 @@ namespace ui { // ClipboardOzone is not yet shipped in production. It is a work in progress // for desktop Linux Wayland support. class ClipboardOzone : public Clipboard { + public: + ClipboardOzone(const ClipboardOzone&) = delete; + ClipboardOzone& operator=(const ClipboardOzone&) = delete; + private: friend class Clipboard; @@ -26,7 +29,8 @@ class ClipboardOzone : public Clipboard { // Clipboard overrides: void OnPreShutdown() override; DataTransferEndpoint* GetSource(ClipboardBuffer buffer) const override; - uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override; + const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const override; bool IsFormatAvailable(const ClipboardFormatType& format, ClipboardBuffer buffer, const DataTransferEndpoint* data_dst) const override; @@ -75,12 +79,11 @@ class ClipboardOzone : public Clipboard { const DataTransferEndpoint* data_dst, std::string* result) const override; bool IsSelectionBufferAvailable() const override; - void WritePortableRepresentations( + void WritePortableTextRepresentation(ClipboardBuffer buffer, + const ObjectMap& objects); + void WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) override; - void WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) override; void WriteText(const char* text_data, size_t text_len) override; @@ -101,15 +104,13 @@ class ClipboardOzone : public Clipboard { const char* data_data, size_t data_len) override; - SkBitmap ReadImageInternal(ClipboardBuffer buffer) const; + std::vector<uint8_t> ReadPngInternal(ClipboardBuffer buffer) const; class AsyncClipboardOzone; std::unique_ptr<AsyncClipboardOzone> async_clipboard_ozone_; base::flat_map<ClipboardBuffer, std::unique_ptr<DataTransferEndpoint>> data_src_; - - DISALLOW_COPY_AND_ASSIGN(ClipboardOzone); }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_sequence_number_token.h b/chromium/ui/base/clipboard/clipboard_sequence_number_token.h new file mode 100644 index 00000000000..b76e1630f60 --- /dev/null +++ b/chromium/ui/base/clipboard/clipboard_sequence_number_token.h @@ -0,0 +1,19 @@ +// Copyright (c) 2021 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 UI_BASE_CLIPBOARD_CLIPBOARD_SEQUENCE_NUMBER_TOKEN_H_ +#define UI_BASE_CLIPBOARD_CLIPBOARD_SEQUENCE_NUMBER_TOKEN_H_ + +#include "base/types/token_type.h" + +namespace ui { + +// Identifies a unique clipboard state. This can be used to version the data on +// the clipboard and determine whether it has changed. +using ClipboardSequenceNumberToken = + base::TokenType<class ClipboardSequenceNumberTokenTypeMarker>; + +} // namespace ui + +#endif // UI_BASE_CLIPBOARD_CLIPBOARD_SEQUENCE_NUMBER_TOKEN_H_ diff --git a/chromium/ui/base/clipboard/clipboard_test_template.h b/chromium/ui/base/clipboard/clipboard_test_template.h index e89e6263a52..494b05094c7 100644 --- a/chromium/ui/base/clipboard/clipboard_test_template.h +++ b/chromium/ui/base/clipboard/clipboard_test_template.h @@ -20,6 +20,7 @@ #include <array> #include <memory> #include <string> +#include <vector> #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" @@ -27,7 +28,6 @@ #include "base/run_loop.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "base/test/scoped_feature_list.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "build/chromecast_buildflags.h" @@ -47,7 +47,7 @@ #include "ui/base/clipboard/test/test_clipboard.h" #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" #include "ui/base/data_transfer_policy/data_transfer_policy_controller.h" -#include "ui/base/ui_base_features.h" +#include "ui/gfx/codec/png_codec.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/half_float.h" #include "url/origin.h" @@ -112,13 +112,15 @@ class MockPolicyController : public DataTransferPolicyController { MockPolicyController(); ~MockPolicyController() override; - MOCK_METHOD2(IsClipboardReadAllowed, + MOCK_METHOD3(IsClipboardReadAllowed, bool(const DataTransferEndpoint* const data_src, - const DataTransferEndpoint* const data_dst)); - MOCK_METHOD4(PasteIfAllowed, + const DataTransferEndpoint* const data_dst, + const absl::optional<size_t> size)); + MOCK_METHOD5(PasteIfAllowed, void(const DataTransferEndpoint* const data_src, const DataTransferEndpoint* const data_dst, - content::WebContents* web_contents, + const absl::optional<size_t> size, + content::RenderFrameHost* rfh, base::OnceCallback<void(bool)> callback)); MOCK_METHOD3(IsDragDropAllowed, bool(const DataTransferEndpoint* const data_src, @@ -149,16 +151,22 @@ TYPED_TEST(ClipboardTest, ClearTest) { EXPECT_TRUE(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste).empty()); EXPECT_FALSE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #if defined(OS_WIN) EXPECT_FALSE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextAType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #endif } -TYPED_TEST(ClipboardTest, TextTest) { +// crbug.com/1224904: Flaky on Mac. +#if defined(OS_MAC) +#define MAYBE_TextTest DISABLED_TextTest +#else +#define MAYBE_TextTest TextTest +#endif +TYPED_TEST(ClipboardTest, MAYBE_TextTest) { std::u16string text(u"This is a std::u16string!#$"), text_result; std::string ascii_text; @@ -180,11 +188,11 @@ TYPED_TEST(ClipboardTest, TextTest) { } #endif EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #if defined(OS_WIN) EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextAType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #endif @@ -211,7 +219,7 @@ TYPED_TEST(ClipboardTest, HTMLTest) { EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste), Contains(ASCIIToUTF16(kMimeTypeHTML))); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); uint32_t fragment_start; uint32_t fragment_end; @@ -237,7 +245,7 @@ TYPED_TEST(ClipboardTest, SvgTest) { } EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetSvgType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::SvgType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); std::u16string markup_result; @@ -264,7 +272,7 @@ TYPED_TEST(ClipboardTest, RTFTest) { EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste), Contains(ASCIIToUTF16(kMimeTypeRTF))); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetRtfType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::RtfType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); std::string result; this->clipboard().ReadRTF(ClipboardBuffer::kCopyPaste, @@ -302,17 +310,17 @@ TYPED_TEST(ClipboardTest, MultipleBufferTest) { Contains(ASCIIToUTF16(kMimeTypeHTML))); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); EXPECT_FALSE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kSelection, + ClipboardFormatType::PlainTextType(), ClipboardBuffer::kSelection, /* data_dst = */ nullptr)); EXPECT_FALSE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kSelection, + ClipboardFormatType::HtmlType(), ClipboardBuffer::kSelection, /* data_dst = */ nullptr)); this->clipboard().ReadText(ClipboardBuffer::kCopyPaste, @@ -345,7 +353,7 @@ TYPED_TEST(ClipboardTest, TrickyHTMLTest) { EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste), Contains(ASCIIToUTF16(kMimeTypeHTML))); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); uint32_t fragment_start; uint32_t fragment_end; @@ -362,9 +370,15 @@ TYPED_TEST(ClipboardTest, TrickyHTMLTest) { #endif // defined(OS_WIN) } +// crbug.com/1224904: Flaky on Mac. +#if defined(OS_MAC) +#define MAYBE_UnicodeHTMLTest DISABLED_UnicodeHTMLTest +#else +#define MAYBE_UnicodeHTMLTest UnicodeHTMLTest +#endif // Some platforms store HTML as UTF-8 internally. Make sure fragment indices are // adjusted appropriately when converting back to UTF-16. -TYPED_TEST(ClipboardTest, UnicodeHTMLTest) { +TYPED_TEST(ClipboardTest, MAYBE_UnicodeHTMLTest) { std::u16string markup(u"<div>A ø 水</div>"), markup_result; std::string url, url_result; @@ -380,7 +394,7 @@ TYPED_TEST(ClipboardTest, UnicodeHTMLTest) { EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste), Contains(ASCIIToUTF16(kMimeTypeHTML))); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); uint32_t fragment_start; uint32_t fragment_end; @@ -407,7 +421,7 @@ TYPED_TEST(ClipboardTest, BookmarkTest) { } EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetUrlType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::UrlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); this->clipboard().ReadBookmark(/* data_dst = */ nullptr, &title_result, &url_result); @@ -417,10 +431,14 @@ TYPED_TEST(ClipboardTest, BookmarkTest) { #endif // !defined(OS_POSIX) || defined(OS_APPLE) #if !defined(OS_ANDROID) +// crbug.com/1224904: Flaky on Mac. +#if defined(OS_MAC) +#define MAYBE_FilenamesTest DISABLED_FilenamesTest +#else +#define MAYBE_FilenamesTest FilenamesTest +#endif // Filenames is not implemented in ClipboardAndroid. -TYPED_TEST(ClipboardTest, FilenamesTest) { - base::test::ScopedFeatureList features; - features.InitWithFeatures({features::kClipboardFilenames}, {}); +TYPED_TEST(ClipboardTest, MAYBE_FilenamesTest) { base::ScopedAllowBlockingForTesting allow_blocking; base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -434,7 +452,7 @@ TYPED_TEST(ClipboardTest, FilenamesTest) { } EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetFilenamesType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::FilenamesType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); std::vector<std::u16string> types; @@ -451,7 +469,13 @@ TYPED_TEST(ClipboardTest, FilenamesTest) { } #endif // !defined(OS_ANDROID) -TYPED_TEST(ClipboardTest, MultiFormatTest) { +// crbug.com/1224904: Flaky on Mac. +#if defined(OS_MAC) +#define MAYBE_MultiFormatTest DISABLED_MultiFormatTest +#else +#define MAYBE_MultiFormatTest MultiFormatTest +#endif +TYPED_TEST(ClipboardTest, MAYBE_MultiFormatTest) { std::u16string text(u"Hi!"), text_result; std::u16string markup(u"<strong>Hi!</string>"), markup_result; std::string url("http://www.example.com/"), url_result; @@ -468,14 +492,14 @@ TYPED_TEST(ClipboardTest, MultiFormatTest) { EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste), Contains(ASCIIToUTF16(kMimeTypeText))); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #if defined(OS_WIN) EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextAType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #endif uint32_t fragment_start; @@ -510,11 +534,11 @@ TYPED_TEST(ClipboardTest, URLTest) { EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste), Contains(ASCIIToUTF16(kMimeTypeText))); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #if defined(OS_WIN) EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextAType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #endif std::u16string text_result; @@ -544,24 +568,21 @@ namespace { using U8x4 = std::array<uint8_t, 4>; using F16x4 = std::array<gfx::HalfFloat, 4>; -template <typename T> -static void TestBitmapWrite(Clipboard* clipboard, - const SkImageInfo& info, - const T* bitmap_data, - const U8x4* expect_data) { +void WriteBitmap(Clipboard* clipboard, + const SkImageInfo& info, + const void* bitmap_data) { { - ScopedClipboardWriter scw(ClipboardBuffer::kCopyPaste); + ScopedClipboardWriter clipboard_writer(ClipboardBuffer::kCopyPaste); SkBitmap bitmap; ASSERT_TRUE(bitmap.setInfo(info)); - bitmap.setPixels( - const_cast<void*>(reinterpret_cast<const void*>(bitmap_data))); - scw.WriteImage(bitmap); + bitmap.setPixels(const_cast<void*>(bitmap_data)); + clipboard_writer.WriteImage(bitmap); } +} - EXPECT_TRUE(clipboard->IsFormatAvailable(ClipboardFormatType::GetBitmapType(), - ClipboardBuffer::kCopyPaste, - /* data_dst = */ nullptr)); - const SkBitmap& image = clipboard_test_util::ReadImage(clipboard); +void AssertBitmapMatchesExpected(const SkBitmap& image, + const SkImageInfo& info, + const U8x4* expect_data) { ASSERT_EQ(image.info().colorType(), kN32_SkColorType); ASSERT_NE(image.info().alphaType(), kUnpremul_SkAlphaType); EXPECT_EQ(gfx::Size(info.width(), info.height()), @@ -576,9 +597,42 @@ static void TestBitmapWrite(Clipboard* clipboard, } } +template <typename T> +static void TestBitmapWrite(Clipboard* clipboard, + const SkImageInfo& info, + const T* bitmap_data, + const U8x4* expect_data) { + WriteBitmap(clipboard, info, reinterpret_cast<const void*>(bitmap_data)); + + EXPECT_TRUE(clipboard->IsFormatAvailable(ClipboardFormatType::BitmapType(), + ClipboardBuffer::kCopyPaste, + /* data_dst = */ nullptr)); + const SkBitmap& image = clipboard_test_util::ReadImage(clipboard); + AssertBitmapMatchesExpected(image, info, expect_data); +} + +template <typename T> +static void TestBitmapWriteAndPngRead(Clipboard* clipboard, + const SkImageInfo& info, + const T* bitmap_data, + const U8x4* expect_data) { + WriteBitmap(clipboard, info, reinterpret_cast<const void*>(bitmap_data)); + + // Expect to be able to read images as either bitmaps or PNGs. + EXPECT_TRUE(clipboard->IsFormatAvailable(ClipboardFormatType::BitmapType(), + ClipboardBuffer::kCopyPaste, + /* data_dst = */ nullptr)); + EXPECT_TRUE(clipboard->IsFormatAvailable(ClipboardFormatType::PngType(), + ClipboardBuffer::kCopyPaste, + /* data_dst = */ nullptr)); + std::vector<uint8_t> result = clipboard_test_util::ReadPng(clipboard); + SkBitmap image; + gfx::PNGCodec::Decode(result.data(), result.size(), &image); + AssertBitmapMatchesExpected(image, info, expect_data); +} + #if !defined(OS_ANDROID) -// TODO(https://crbug.com/1056650): Re-enable these tests after fixing the root -// cause. This test only fails on Android. +// TODO(crbug.com/815537): Re-enable this test once death tests work on Android. // Only kN32_SkColorType bitmaps are allowed in the clipboard to prevent // surprising buffer overflows due to bits-per-pixel assumptions. @@ -591,8 +645,15 @@ TYPED_TEST(ClipboardTest, Bitmap_F16_Premul) { &kRGBAF16Premul, &kRGBAPremul), ""); } +#endif // !defined(OS_ANDROID) -TYPED_TEST(ClipboardTest, Bitmap_N32_Premul) { +// crbug.com/1224904: Flaky on Mac. +#if defined(OS_MAC) +#define MAYBE_Bitmap_N32_Premul DISABLED_Bitmap_N32_Premul +#else +#define MAYBE_Bitmap_N32_Premul Bitmap_N32_Premul +#endif +TYPED_TEST(ClipboardTest, MAYBE_Bitmap_N32_Premul) { constexpr U8x4 b[4 * 3] = { {0x26, 0x16, 0x06, 0x46}, {0x88, 0x59, 0x9f, 0xf6}, {0x37, 0x29, 0x3f, 0x79}, {0x86, 0xb9, 0x55, 0xfa}, @@ -603,6 +664,7 @@ TYPED_TEST(ClipboardTest, Bitmap_N32_Premul) { }; TestBitmapWrite(&this->clipboard(), SkImageInfo::MakeN32Premul(4, 3), b, b); } + TYPED_TEST(ClipboardTest, Bitmap_N32_Premul_2x7) { constexpr U8x4 b[2 * 7] = { {0x26, 0x16, 0x06, 0x46}, {0x88, 0x59, 0x9f, 0xf6}, @@ -615,8 +677,51 @@ TYPED_TEST(ClipboardTest, Bitmap_N32_Premul_2x7) { }; TestBitmapWrite(&this->clipboard(), SkImageInfo::MakeN32Premul(2, 7), b, b); } + +#if !defined(OS_ANDROID) +// TODO(crbug.com/815537): Re-enable this test once death tests work on Android. + +// Only kN32_SkColorType bitmaps are allowed into the clipboard to prevent +// surprising buffer overflows due to bits-per-pixel assumptions. +TYPED_TEST(ClipboardTest, BitmapWriteAndPngRead_F16_Premul) { + constexpr F16x4 kRGBAF16Premul = {0x30c5, 0x2d86, 0x2606, 0x3464}; + constexpr U8x4 kRGBAPremul = {0x26, 0x16, 0x06, 0x46}; + EXPECT_DEATH( + TestBitmapWriteAndPngRead( + &this->clipboard(), + SkImageInfo::Make(1, 1, kRGBA_F16_SkColorType, kPremul_SkAlphaType), + &kRGBAF16Premul, &kRGBAPremul), + ""); +} #endif // !defined(OS_ANDROID) +TYPED_TEST(ClipboardTest, BitmapWriteAndPngRead_N32_Premul) { + constexpr U8x4 b[4 * 3] = { + {0x26, 0x16, 0x06, 0x46}, {0x88, 0x59, 0x9f, 0xf6}, + {0x37, 0x29, 0x3f, 0x79}, {0x86, 0xb9, 0x55, 0xfa}, + {0x52, 0x21, 0x77, 0x78}, {0x30, 0x2a, 0x69, 0x87}, + {0x25, 0x2a, 0x32, 0x36}, {0x1b, 0x40, 0x20, 0x43}, + {0x21, 0x8c, 0x84, 0x91}, {0x3c, 0x7b, 0x17, 0xc3}, + {0x5c, 0x15, 0x46, 0x69}, {0x52, 0x19, 0x17, 0x64}, + }; + TestBitmapWriteAndPngRead(&this->clipboard(), + SkImageInfo::MakeN32Premul(4, 3), b, b); +} + +TYPED_TEST(ClipboardTest, BitmapWriteAndPngRead_N32_Premul_2x7) { + constexpr U8x4 b[2 * 7] = { + {0x26, 0x16, 0x06, 0x46}, {0x88, 0x59, 0x9f, 0xf6}, + {0x37, 0x29, 0x3f, 0x79}, {0x86, 0xb9, 0x55, 0xfa}, + {0x52, 0x21, 0x77, 0x78}, {0x30, 0x2a, 0x69, 0x87}, + {0x25, 0x2a, 0x32, 0x36}, {0x1b, 0x40, 0x20, 0x43}, + {0x21, 0x8c, 0x84, 0x91}, {0x3c, 0x7b, 0x17, 0xc3}, + {0x5c, 0x15, 0x46, 0x69}, {0x52, 0x19, 0x17, 0x64}, + {0x13, 0x03, 0x91, 0xa6}, {0x3e, 0x32, 0x02, 0x83}, + }; + TestBitmapWriteAndPngRead(&this->clipboard(), + SkImageInfo::MakeN32Premul(2, 7), b, b); +} + } // namespace TYPED_TEST(ClipboardTest, PickleTest) { @@ -637,7 +742,7 @@ TYPED_TEST(ClipboardTest, PickleTest) { this->clipboard().ReadData(kFormat, /* data_dst = */ nullptr, &output); ASSERT_FALSE(output.empty()); - base::Pickle read_pickle(output.data(), static_cast<int>(output.size())); + base::Pickle read_pickle(output.data(), output.size()); base::PickleIterator iter(read_pickle); std::string unpickled_string; ASSERT_TRUE(iter.ReadString(&unpickled_string)); @@ -674,7 +779,7 @@ TYPED_TEST(ClipboardTest, MultiplePickleTest) { this->clipboard().ReadData(kFormat2, /* data_dst = */ nullptr, &output2); ASSERT_FALSE(output2.empty()); - base::Pickle read_pickle2(output2.data(), static_cast<int>(output2.size())); + base::Pickle read_pickle2(output2.data(), output2.size()); base::PickleIterator iter2(read_pickle2); std::string unpickled_string2; ASSERT_TRUE(iter2.ReadString(&unpickled_string2)); @@ -697,18 +802,18 @@ TYPED_TEST(ClipboardTest, MultiplePickleTest) { this->clipboard().ReadData(kFormat1, /* data_dst = */ nullptr, &output1); ASSERT_FALSE(output1.empty()); - base::Pickle read_pickle1(output1.data(), static_cast<int>(output1.size())); + base::Pickle read_pickle1(output1.data(), output1.size()); base::PickleIterator iter1(read_pickle1); std::string unpickled_string1; ASSERT_TRUE(iter1.ReadString(&unpickled_string1)); 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::GetType(kFormatString); const std::string payload = "test string"; base::span<const uint8_t> payload_span( reinterpret_cast<const uint8_t*>(payload.data()), payload.size()); @@ -719,33 +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); } -// TODO(https://crbug.com/1032161): Implement multiple raw types for -// ClipboardInternal. This test currently doesn't run on ClipboardInternal -// because ClipboardInternal only supports one raw type. -#if (!defined(USE_AURA) || defined(OS_WIN) || defined(USE_OZONE) || \ - defined(USE_X11)) && \ - !BUILDFLAG(IS_CHROMEOS_ASH) 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::GetType(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::GetType(kFormatString2); const std::string payload2("test string2"); base::span<const uint8_t> payload_span2( reinterpret_cast<const uint8_t*>(payload2.data()), payload2.size()); @@ -763,29 +863,112 @@ 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 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 std::string payload2("test string2"); + base::span<const uint8_t> payload_span2( + reinterpret_cast<const uint8_t*>(payload2.data()), payload2.size()); + + { + ScopedClipboardWriter clipboard_writer(ClipboardBuffer::kCopyPaste); + // Both payloads should write successfully and not overwrite one another. + clipboard_writer.WriteData(kFormatString116, + mojo_base::BigBuffer(payload_span1)); + clipboard_writer.WriteData(kFormatString216, + mojo_base::BigBuffer(payload_span2)); + } + + // Check format 1. + EXPECT_THAT(this->clipboard().ReadAvailablePlatformSpecificFormatNames( + ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr), + Contains(kFormatString116)); + 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(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(custom_format_names.find(kFormatString2) != + custom_format_names.end()); + std::string output2; + this->clipboard().ReadData(ClipboardFormatType::CustomPlatformType( + custom_format_names[kFormatString2]), + /* data_dst = */ nullptr, &output2); + EXPECT_EQ(payload2, output2); +} +#endif + +// crbug.com/1224904: Flaky on Mac. +#if defined(OS_MAC) +#define MAYBE_ReadAvailablePlatformSpecificFormatNamesTest \ + DISABLED_ReadAvailablePlatformSpecificFormatNamesTest +#else +#define MAYBE_ReadAvailablePlatformSpecificFormatNamesTest \ + ReadAvailablePlatformSpecificFormatNamesTest #endif +TYPED_TEST(ClipboardTest, MAYBE_ReadAvailablePlatformSpecificFormatNamesTest) { + // We're testing platform-specific behavior, so use PlatformClipboardTest. + // TODO(https://crbug.com/1083050): The template shouldn't know about its + // instantiations. Move this information up using a flag, virtual method, or + // creating separate test files for different platforms. + std::string test_suite_name = ::testing::UnitTest::GetInstance() + ->current_test_info() + ->test_suite_name(); + // TODO(crbug.com/106449): Update other platforms to support custom formats. + if (test_suite_name != std::string("ClipboardTest/PlatformClipboardTest")) + return; -TYPED_TEST(ClipboardTest, ReadAvailablePlatformSpecificFormatNamesTest) { std::u16string text = u"Test String"; std::string ascii_text; { ScopedClipboardWriter clipboard_writer(ClipboardBuffer::kCopyPaste); + // `WriteText` uses `ClipboardFormatType::PlainTextType` format. clipboard_writer.WriteText(text); } @@ -795,7 +978,7 @@ TYPED_TEST(ClipboardTest, ReadAvailablePlatformSpecificFormatNamesTest) { #if defined(OS_APPLE) EXPECT_THAT(raw_types, Contains(u"public.utf8-plain-text")); EXPECT_THAT(raw_types, Contains(u"NSStringPboardType")); - EXPECT_EQ(raw_types.size(), static_cast<uint64_t>(2)); + EXPECT_EQ(raw_types.size(), 2u); // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch // of lacros-chrome is complete. #elif defined(OS_LINUX) && !BUILDFLAG(IS_CHROMEOS_ASH) && \ @@ -807,22 +990,17 @@ TYPED_TEST(ClipboardTest, ReadAvailablePlatformSpecificFormatNamesTest) { #if defined(USE_OZONE) if (features::IsUsingOzonePlatform()) { EXPECT_THAT(raw_types, Contains(ASCIIToUTF16(kMimeTypeTextUtf8))); - EXPECT_EQ(raw_types.size(), static_cast<uint64_t>(5)); + EXPECT_EQ(raw_types.size(), 5u); return; } #endif // USE_OZONE #if defined(USE_X11) EXPECT_FALSE(features::IsUsingOzonePlatform()); - EXPECT_EQ(raw_types.size(), static_cast<uint64_t>(4)); + EXPECT_EQ(raw_types.size(), 4u); #endif // USE_X11 -#elif defined(OS_WIN) - EXPECT_THAT(raw_types, Contains(u"CF_UNICODETEXT")); - EXPECT_THAT(raw_types, Contains(u"CF_TEXT")); - EXPECT_THAT(raw_types, Contains(u"CF_OEMTEXT")); - EXPECT_EQ(raw_types.size(), static_cast<uint64_t>(3)); -#elif defined(USE_AURA) || defined(OS_ANDROID) +#elif defined(USE_AURA) || defined(OS_ANDROID) || defined(OS_WIN) EXPECT_THAT(raw_types, Contains(ASCIIToUTF16(kMimeTypeText))); - EXPECT_EQ(raw_types.size(), static_cast<uint64_t>(1)); + EXPECT_EQ(raw_types.size(), 1u); #else #error Unsupported platform #endif @@ -845,13 +1023,11 @@ TYPED_TEST(ClipboardTest, PlatformSpecificDataTest) { return; const std::string text = "test string"; + const std::string kFormatString = "text/plain"; #if defined(OS_WIN) - // Windows pre-defined ANSI text format. - const std::string kFormatString = "CF_TEXT"; // Windows requires an extra '\0' at the end for a raw write. const std::string kPlatformSpecificText = text + '\0'; #elif defined(USE_X11) - const std::string kFormatString = "text/plain"; // X11 text format const std::string kPlatformSpecificText = text; #endif base::span<const uint8_t> text_span( @@ -863,38 +1039,18 @@ TYPED_TEST(ClipboardTest, PlatformSpecificDataTest) { mojo_base::BigBuffer(text_span)); } - const std::vector<std::u16string> raw_types = - this->clipboard().ReadAvailablePlatformSpecificFormatNames( - ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr); - - EXPECT_THAT(raw_types, Contains(ASCIIToUTF16(kFormatString))); - -#if defined(OS_WIN) - // Only Windows ClipboardFormatType recognizes ANSI formats. - EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste, - /* data_dst = */ nullptr)); -#endif // defined(OS_WIN) - - EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), 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::GetPlainTextType(), 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)); + 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(kFormatString) != + custom_format_names.end()); std::string platform_specific_result; - this->clipboard().ReadData(ClipboardFormatType::GetType(kFormatString), + this->clipboard().ReadData(ClipboardFormatType::CustomPlatformType( + custom_format_names[kFormatString]), /* data_dst = */ nullptr, &platform_specific_result); EXPECT_EQ(platform_specific_result, kPlatformSpecificText); @@ -917,7 +1073,7 @@ TYPED_TEST(ClipboardTest, HyperlinkTest) { } EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); uint32_t fragment_start; uint32_t fragment_end; @@ -937,8 +1093,8 @@ TYPED_TEST(ClipboardTest, WebSmartPasteTest) { } EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetWebKitSmartPasteType(), - ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); + ClipboardFormatType::WebKitSmartPasteType(), ClipboardBuffer::kCopyPaste, + /* data_dst = */ nullptr)); } #if defined(OS_WIN) // Windows only tests. @@ -1011,7 +1167,7 @@ TYPED_TEST(ClipboardTest, WriteEverything) { // written to. // TODO(dcheng): Add a version to test ClipboardBuffer::kSelection. TYPED_TEST(ClipboardTest, GetSequenceNumber) { - const uint64_t first_sequence_number = + const ClipboardSequenceNumberToken first_sequence_number = this->clipboard().GetSequenceNumber(ClipboardBuffer::kCopyPaste); { @@ -1023,7 +1179,7 @@ TYPED_TEST(ClipboardTest, GetSequenceNumber) { // the message loop to make sure we get the notification. base::RunLoop().RunUntilIdle(); - const uint64_t second_sequence_number = + const ClipboardSequenceNumberToken second_sequence_number = this->clipboard().GetSequenceNumber(ClipboardBuffer::kCopyPaste); EXPECT_NE(first_sequence_number, second_sequence_number); @@ -1064,7 +1220,7 @@ TYPED_TEST(ClipboardTest, WriteHyperlinkEmptyParams) { TYPED_TEST(ClipboardTest, WritePickledData) { ScopedClipboardWriter scw(ClipboardBuffer::kCopyPaste); - scw.WritePickledData(base::Pickle(), ClipboardFormatType::GetPlainTextType()); + scw.WritePickledData(base::Pickle(), ClipboardFormatType::PlainTextType()); } TYPED_TEST(ClipboardTest, WriteImageEmptyParams) { diff --git a/chromium/ui/base/clipboard/clipboard_util_mac_unittest.mm b/chromium/ui/base/clipboard/clipboard_util_mac_unittest.mm index 13a2ef789a9..1156d26a91e 100644 --- a/chromium/ui/base/clipboard/clipboard_util_mac_unittest.mm +++ b/chromium/ui/base/clipboard/clipboard_util_mac_unittest.mm @@ -6,6 +6,7 @@ #include "base/mac/scoped_nsobject.h" #include "base/memory/ref_counted.h" +#include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest_mac.h" #include "testing/platform_test.h" @@ -18,13 +19,28 @@ class ClipboardUtilMacTest : public PlatformTest { public: ClipboardUtilMacTest() { } - NSDictionary* DictionaryFromPasteboard(NSPasteboard* pboard) { - NSArray* types = [pboard types]; - NSMutableDictionary* data = [NSMutableDictionary dictionary]; - for (NSString* type in types) { - data[type] = [pboard dataForType:type]; + // Given a pasteboard, returns a dictionary of the contents of the pasteboard + // for use in deep comparisons. This fully unpacks any plist-encoded items. + NSDictionary* DictionaryFromPasteboardForDeepComparisons( + NSPasteboard* pboard) { + NSMutableDictionary* result = [NSMutableDictionary dictionary]; + for (NSString* type in [pboard types]) { + NSData* data = [pboard dataForType:type]; + // Try to unpack the data as a plist, and if it succeeds, use that in the + // resulting dictionary rather than the raw NSData. This is needed because + // plists have multiple encodings, and the comparison should be made on + // the underlying data rather than the specific encoding used by the OS. + NSDictionary* unpacked_data = [NSPropertyListSerialization + propertyListWithData:data + options:NSPropertyListImmutable + format:nil + error:nil]; + if (unpacked_data) + result[type] = unpacked_data; + else + result[type] = data; } - return data; + return result; } }; @@ -126,8 +142,10 @@ TEST_F(ClipboardUtilMacTest, CompareToWriteToPasteboard) { scoped_refptr<UniquePasteboard> pboard = new UniquePasteboard; [pboard->get() setDataForURL:urlString title:urlString]; - NSDictionary* data1 = DictionaryFromPasteboard(pasteboard->get()); - NSDictionary* data2 = DictionaryFromPasteboard(pboard->get()); + NSDictionary* data1 = + DictionaryFromPasteboardForDeepComparisons(pasteboard->get()); + NSDictionary* data2 = + DictionaryFromPasteboardForDeepComparisons(pboard->get()); EXPECT_NSEQ(data1, data2); } diff --git a/chromium/ui/base/clipboard/clipboard_util_win.cc b/chromium/ui/base/clipboard/clipboard_util_win.cc index 747923cb6d4..4acce28b16a 100644 --- a/chromium/ui/base/clipboard/clipboard_util_win.cc +++ b/chromium/ui/base/clipboard/clipboard_util_win.cc @@ -11,9 +11,9 @@ #include <limits> #include <utility> +#include "base/cxx17_backports.h" #include "base/files/file_util.h" #include "base/logging.h" -#include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/sys_string_conversions.h" @@ -55,7 +55,7 @@ bool GetUrlFromHDrop(IDataObject* data_object, bool success = false; STGMEDIUM medium; - if (!GetData(data_object, ClipboardFormatType::GetCFHDropType(), &medium)) + if (!GetData(data_object, ClipboardFormatType::CFHDropType(), &medium)) return false; { @@ -250,13 +250,12 @@ HGLOBAL CopyFileContentsToHGlobal(IDataObject* data_object, LONG index) { DCHECK(data_object); HGLOBAL hdata = nullptr; - if (!HasData(data_object, - ClipboardFormatType::GetFileContentAtIndexType(index))) + if (!HasData(data_object, ClipboardFormatType::FileContentAtIndexType(index))) return hdata; STGMEDIUM content; - if (!GetData(data_object, - ClipboardFormatType::GetFileContentAtIndexType(index), &content)) + if (!GetData(data_object, ClipboardFormatType::FileContentAtIndexType(index), + &content)) return hdata; HRESULT hr = S_OK; @@ -365,7 +364,7 @@ struct FileGroupDescriptorData; template <> struct FileGroupDescriptorData<FILEGROUPDESCRIPTORW> { static bool get(IDataObject* data_object, STGMEDIUM* medium) { - return GetData(data_object, ClipboardFormatType::GetFileDescriptorType(), + return GetData(data_object, ClipboardFormatType::FileDescriptorType(), medium); } }; @@ -373,7 +372,7 @@ struct FileGroupDescriptorData<FILEGROUPDESCRIPTORW> { template <> struct FileGroupDescriptorData<FILEGROUPDESCRIPTORA> { static bool get(IDataObject* data_object, STGMEDIUM* medium) { - return GetData(data_object, ClipboardFormatType::GetFileDescriptorAType(), + return GetData(data_object, ClipboardFormatType::FileDescriptorAType(), medium); } }; @@ -448,46 +447,45 @@ bool GetFileNameFromFirstDescriptor(IDataObject* data_object, bool ClipboardUtil::HasUrl(IDataObject* data_object, bool convert_filenames) { DCHECK(data_object); - return HasData(data_object, ClipboardFormatType::GetMozUrlType()) || - HasData(data_object, ClipboardFormatType::GetUrlType()) || - HasData(data_object, ClipboardFormatType::GetUrlAType()) || + return HasData(data_object, ClipboardFormatType::MozUrlType()) || + HasData(data_object, ClipboardFormatType::UrlType()) || + HasData(data_object, ClipboardFormatType::UrlAType()) || (convert_filenames && HasFilenames(data_object)); } bool ClipboardUtil::HasFilenames(IDataObject* data_object) { DCHECK(data_object); - return HasData(data_object, ClipboardFormatType::GetCFHDropType()) || - HasData(data_object, ClipboardFormatType::GetFilenameType()) || - HasData(data_object, ClipboardFormatType::GetFilenameAType()); + return HasData(data_object, ClipboardFormatType::CFHDropType()) || + HasData(data_object, ClipboardFormatType::FilenameType()) || + HasData(data_object, ClipboardFormatType::FilenameAType()); } bool ClipboardUtil::HasVirtualFilenames(IDataObject* data_object) { DCHECK(data_object); // Favor real files on the file system over virtual files. return !HasFilenames(data_object) && - HasData(data_object, - ClipboardFormatType::GetFileContentAtIndexType(0)) && - (HasData(data_object, ClipboardFormatType::GetFileDescriptorType()) || - HasData(data_object, ClipboardFormatType::GetFileDescriptorAType())); + HasData(data_object, ClipboardFormatType::FileContentAtIndexType(0)) && + (HasData(data_object, ClipboardFormatType::FileDescriptorType()) || + HasData(data_object, ClipboardFormatType::FileDescriptorAType())); } bool ClipboardUtil::HasFileContents(IDataObject* data_object) { DCHECK(data_object); - return HasData(data_object, ClipboardFormatType::GetFileContentZeroType()) && - (HasData(data_object, ClipboardFormatType::GetFileDescriptorType()) || - HasData(data_object, ClipboardFormatType::GetFileDescriptorAType())); + return HasData(data_object, ClipboardFormatType::FileContentZeroType()) && + (HasData(data_object, ClipboardFormatType::FileDescriptorType()) || + HasData(data_object, ClipboardFormatType::FileDescriptorAType())); } bool ClipboardUtil::HasHtml(IDataObject* data_object) { DCHECK(data_object); - return HasData(data_object, ClipboardFormatType::GetHtmlType()) || - HasData(data_object, ClipboardFormatType::GetTextHtmlType()); + return HasData(data_object, ClipboardFormatType::HtmlType()) || + HasData(data_object, ClipboardFormatType::TextHtmlType()); } bool ClipboardUtil::HasPlainText(IDataObject* data_object) { DCHECK(data_object); - return HasData(data_object, ClipboardFormatType::GetPlainTextType()) || - HasData(data_object, ClipboardFormatType::GetPlainTextAType()); + return HasData(data_object, ClipboardFormatType::PlainTextType()) || + HasData(data_object, ClipboardFormatType::PlainTextAType()); } bool ClipboardUtil::GetUrl(IDataObject* data_object, @@ -503,8 +501,8 @@ bool ClipboardUtil::GetUrl(IDataObject* data_object, if (GetUrlFromHDrop(data_object, url, title)) return true; - if (GetData(data_object, ClipboardFormatType::GetMozUrlType(), &store) || - GetData(data_object, ClipboardFormatType::GetUrlType(), &store)) { + if (GetData(data_object, ClipboardFormatType::MozUrlType(), &store) || + GetData(data_object, ClipboardFormatType::UrlType(), &store)) { { // Mozilla URL format or Unicode URL base::win::ScopedHGlobal<wchar_t*> data(store.hGlobal); @@ -514,7 +512,7 @@ bool ClipboardUtil::GetUrl(IDataObject* data_object, return url->is_valid(); } - if (GetData(data_object, ClipboardFormatType::GetUrlAType(), &store)) { + if (GetData(data_object, ClipboardFormatType::UrlAType(), &store)) { { // URL using ASCII base::win::ScopedHGlobal<char*> data(store.hGlobal); @@ -543,7 +541,7 @@ bool ClipboardUtil::GetFilenames(IDataObject* data_object, return false; STGMEDIUM medium; - if (GetData(data_object, ClipboardFormatType::GetCFHDropType(), &medium)) { + if (GetData(data_object, ClipboardFormatType::CFHDropType(), &medium)) { { base::win::ScopedHGlobal<HDROP> hdrop(medium.hGlobal); if (!hdrop.get()) @@ -562,7 +560,7 @@ bool ClipboardUtil::GetFilenames(IDataObject* data_object, return !filenames->empty(); } - if (GetData(data_object, ClipboardFormatType::GetFilenameType(), &medium)) { + if (GetData(data_object, ClipboardFormatType::FilenameType(), &medium)) { { // filename using Unicode base::win::ScopedHGlobal<wchar_t*> data(medium.hGlobal); @@ -573,7 +571,7 @@ bool ClipboardUtil::GetFilenames(IDataObject* data_object, return true; } - if (GetData(data_object, ClipboardFormatType::GetFilenameAType(), &medium)) { + if (GetData(data_object, ClipboardFormatType::FilenameAType(), &medium)) { { // filename using ASCII base::win::ScopedHGlobal<char*> data(medium.hGlobal); @@ -692,7 +690,7 @@ bool ClipboardUtil::GetPlainText(IDataObject* data_object, return false; STGMEDIUM store; - if (GetData(data_object, ClipboardFormatType::GetPlainTextType(), &store)) { + if (GetData(data_object, ClipboardFormatType::PlainTextType(), &store)) { { // Unicode text base::win::ScopedHGlobal<wchar_t*> data(store.hGlobal); @@ -702,7 +700,7 @@ bool ClipboardUtil::GetPlainText(IDataObject* data_object, return true; } - if (GetData(data_object, ClipboardFormatType::GetPlainTextAType(), &store)) { + if (GetData(data_object, ClipboardFormatType::PlainTextAType(), &store)) { { // ASCII text base::win::ScopedHGlobal<char*> data(store.hGlobal); @@ -729,8 +727,8 @@ bool ClipboardUtil::GetHtml(IDataObject* data_object, DCHECK(data_object && html && base_url); STGMEDIUM store; - if (HasData(data_object, ClipboardFormatType::GetHtmlType()) && - GetData(data_object, ClipboardFormatType::GetHtmlType(), &store)) { + if (HasData(data_object, ClipboardFormatType::HtmlType()) && + GetData(data_object, ClipboardFormatType::HtmlType(), &store)) { { // MS CF html base::win::ScopedHGlobal<char*> data(store.hGlobal); @@ -743,10 +741,10 @@ bool ClipboardUtil::GetHtml(IDataObject* data_object, return true; } - if (!HasData(data_object, ClipboardFormatType::GetTextHtmlType())) + if (!HasData(data_object, ClipboardFormatType::TextHtmlType())) return false; - if (!GetData(data_object, ClipboardFormatType::GetTextHtmlType(), &store)) + if (!GetData(data_object, ClipboardFormatType::TextHtmlType(), &store)) return false; { @@ -768,7 +766,7 @@ bool ClipboardUtil::GetFileContents(IDataObject* data_object, STGMEDIUM content; // The call to GetData can be very slow depending on what is in // |data_object|. - if (GetData(data_object, ClipboardFormatType::GetFileContentZeroType(), + if (GetData(data_object, ClipboardFormatType::FileContentZeroType(), &content)) { if (TYMED_HGLOBAL == content.tymed) { base::win::ScopedHGlobal<char*> data(content.hGlobal); @@ -800,12 +798,11 @@ bool ClipboardUtil::GetWebCustomData( std::unordered_map<std::u16string, std::u16string>* custom_data) { DCHECK(data_object && custom_data); - if (!HasData(data_object, ClipboardFormatType::GetWebCustomDataType())) + if (!HasData(data_object, ClipboardFormatType::WebCustomDataType())) return false; STGMEDIUM store; - if (GetData(data_object, ClipboardFormatType::GetWebCustomDataType(), - &store)) { + if (GetData(data_object, ClipboardFormatType::WebCustomDataType(), &store)) { { base::win::ScopedHGlobal<char*> data(store.hGlobal); ReadCustomDataIntoMap(data.get(), data.Size(), custom_data); diff --git a/chromium/ui/base/clipboard/clipboard_win.cc b/chromium/ui/base/clipboard/clipboard_win.cc index 868de231d10..9422954679f 100644 --- a/chromium/ui/base/clipboard/clipboard_win.cc +++ b/chromium/ui/base/clipboard/clipboard_win.cc @@ -10,16 +10,15 @@ #include <objidl.h> #include <shellapi.h> #include <shlobj.h> +#include <cstdint> +#include <vector> #include "base/bind.h" #include "base/check_op.h" -#include "base/feature_list.h" #include "base/files/file_path.h" #include "base/lazy_instance.h" -#include "base/macros.h" #include "base/notreached.h" #include "base/numerics/safe_conversions.h" -#include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" @@ -40,7 +39,6 @@ #include "ui/base/clipboard/clipboard_util_win.h" #include "ui/base/clipboard/custom_data_helper.h" #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" -#include "ui/base/ui_base_features.h" #include "ui/gfx/canvas.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/geometry/size.h" @@ -56,7 +54,8 @@ class AnonymousImpersonator { AnonymousImpersonator() { must_revert_ = ::ImpersonateAnonymousToken(::GetCurrentThread()); } - + AnonymousImpersonator(const AnonymousImpersonator&) = delete; + AnonymousImpersonator& operator=(const AnonymousImpersonator&) = delete; ~AnonymousImpersonator() { if (must_revert_) ::RevertToSelf(); @@ -64,7 +63,6 @@ class AnonymousImpersonator { private: BOOL must_revert_; - DISALLOW_COPY_AND_ASSIGN(AnonymousImpersonator); }; // A scoper to manage acquiring and automatically releasing the clipboard. @@ -225,16 +223,12 @@ void TrimAfterNull(StringType* result) { } bool ReadFilenamesAvailable() { - // Only support filenames if chrome://flags#clipboard-filenames is enabled. - if (!base::FeatureList::IsEnabled(features::kClipboardFilenames)) - return false; - return ::IsClipboardFormatAvailable( - ClipboardFormatType::GetCFHDropType().ToFormatEtc().cfFormat) || + ClipboardFormatType::CFHDropType().ToFormatEtc().cfFormat) || ::IsClipboardFormatAvailable( - ClipboardFormatType::GetFilenameType().ToFormatEtc().cfFormat) || + ClipboardFormatType::FilenameType().ToFormatEtc().cfFormat) || ::IsClipboardFormatAvailable( - ClipboardFormatType::GetFilenameAType().ToFormatEtc().cfFormat); + ClipboardFormatType::FilenameAType().ToFormatEtc().cfFormat); } } // namespace @@ -262,9 +256,16 @@ DataTransferEndpoint* ClipboardWin::GetSource(ClipboardBuffer buffer) const { return nullptr; } -uint64_t ClipboardWin::GetSequenceNumber(ClipboardBuffer buffer) const { +const ClipboardSequenceNumberToken& ClipboardWin::GetSequenceNumber( + ClipboardBuffer buffer) const { DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - return ::GetClipboardSequenceNumber(); + + DWORD sequence_number = ::GetClipboardSequenceNumber(); + if (sequence_number != clipboard_sequence_.sequence_number) { + // Generate a unique token associated with the current sequence number. + clipboard_sequence_ = {sequence_number, ClipboardSequenceNumberToken()}; + } + return clipboard_sequence_.token; } // |data_dst| is not used. It's only passed to be consistent with other @@ -274,8 +275,19 @@ bool ClipboardWin::IsFormatAvailable( ClipboardBuffer buffer, const DataTransferEndpoint* data_dst) const { DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - if (format == ClipboardFormatType::GetFilenameType()) + if (format == ClipboardFormatType::FilenameType()) return ReadFilenamesAvailable(); + // Chrome can retrieve an image from the clipboard as either a bitmap or PNG. + if (format == ClipboardFormatType::PngType() || + format == ClipboardFormatType::BitmapType()) { + return ::IsClipboardFormatAvailable( + ClipboardFormatType::PngType().ToFormatEtc().cfFormat) != + FALSE || + ::IsClipboardFormatAvailable( + ClipboardFormatType::BitmapType().ToFormatEtc().cfFormat) != + FALSE; + } + return ::IsClipboardFormatAvailable(format.ToFormatEtc().cfFormat) != FALSE; } @@ -298,13 +310,13 @@ void ClipboardWin::ReadAvailableTypes( types->clear(); if (::IsClipboardFormatAvailable( - ClipboardFormatType::GetPlainTextAType().ToFormatEtc().cfFormat)) + ClipboardFormatType::PlainTextAType().ToFormatEtc().cfFormat)) types->push_back(base::UTF8ToUTF16(kMimeTypeText)); if (::IsClipboardFormatAvailable( - ClipboardFormatType::GetHtmlType().ToFormatEtc().cfFormat)) + ClipboardFormatType::HtmlType().ToFormatEtc().cfFormat)) types->push_back(base::UTF8ToUTF16(kMimeTypeHTML)); if (::IsClipboardFormatAvailable( - ClipboardFormatType::GetRtfType().ToFormatEtc().cfFormat)) + ClipboardFormatType::RtfType().ToFormatEtc().cfFormat)) types->push_back(base::UTF8ToUTF16(kMimeTypeRTF)); if (::IsClipboardFormatAvailable(CF_DIB)) types->push_back(base::UTF8ToUTF16(kMimeTypePNG)); @@ -317,7 +329,7 @@ void ClipboardWin::ReadAvailableTypes( return; HANDLE hdata = ::GetClipboardData( - ClipboardFormatType::GetWebCustomDataType().ToFormatEtc().cfFormat); + ClipboardFormatType::WebCustomDataType().ToFormatEtc().cfFormat); if (!hdata) return; @@ -342,12 +354,18 @@ 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(); if (!type_name.empty()) - types.push_back(base::UTF8ToUTF16(type_name)); + types.push_back(base::ASCIIToUTF16(type_name)); cf_format = ::EnumClipboardFormats(cf_format); } return types; @@ -436,7 +454,7 @@ void ClipboardWin::ReadHTML(ClipboardBuffer buffer, return; HANDLE data = ::GetClipboardData( - ClipboardFormatType::GetHtmlType().ToFormatEtc().cfFormat); + ClipboardFormatType::HtmlType().ToFormatEtc().cfFormat); if (!data) return; @@ -461,9 +479,8 @@ void ClipboardWin::ReadHTML(ClipboardBuffer buffer, if (start_index < html_start || end_index < start_index) return; - std::vector<size_t> offsets; - offsets.push_back(start_index - html_start); - offsets.push_back(end_index - html_start); + std::vector<size_t> offsets = {start_index - html_start, + end_index - html_start}; markup->assign(base::UTF8ToUTF16AndAdjustOffsets(cf_html.data() + html_start, &offsets)); // Ensure the Fragment points within the string; see https://crbug.com/607181. @@ -481,7 +498,7 @@ void ClipboardWin::ReadSvg(ClipboardBuffer buffer, RecordRead(ClipboardFormatMetric::kSvg); std::string data; - ReadData(ClipboardFormatType::GetSvgType(), data_dst, &data); + ReadData(ClipboardFormatType::SvgType(), data_dst, &data); result->assign(reinterpret_cast<const char16_t*>(data.data()), data.size() / sizeof(char16_t)); @@ -496,7 +513,7 @@ void ClipboardWin::ReadRTF(ClipboardBuffer buffer, DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); RecordRead(ClipboardFormatMetric::kRtf); - ReadData(ClipboardFormatType::GetRtfType(), data_dst, result); + ReadData(ClipboardFormatType::RtfType(), data_dst, result); TrimAfterNull(result); } @@ -505,8 +522,16 @@ void ClipboardWin::ReadRTF(ClipboardBuffer buffer, void ClipboardWin::ReadPng(ClipboardBuffer buffer, const DataTransferEndpoint* data_dst, ReadPngCallback callback) const { - // TODO(crbug.com/1201018): Implement this. - NOTIMPLEMENTED(); + RecordRead(ClipboardFormatMetric::kPng); + std::vector<uint8_t> data = ReadPngInternal(buffer); + // On Windows, PNG and bitmap are separate formats. Read PNG if possible, + // otherwise fall back to reading as a bitmap. + if (data.empty()) { + SkBitmap bitmap = ReadImageInternal(buffer); + gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, /*discard_transparency=*/false, + &data); + } + std::move(callback).Run(data); } // |data_dst| is not used. It's only passed to be consistent with other @@ -533,7 +558,7 @@ void ClipboardWin::ReadCustomData(ClipboardBuffer buffer, return; HANDLE hdata = ::GetClipboardData( - ClipboardFormatType::GetWebCustomDataType().ToFormatEtc().cfFormat); + ClipboardFormatType::WebCustomDataType().ToFormatEtc().cfFormat); if (!hdata) return; @@ -562,7 +587,7 @@ void ClipboardWin::ReadFilenames(ClipboardBuffer buffer, // TODO(crbug.com/1178671): Refactor similar code in clipboard_utils_win: // ClipboardUtil::GetFilenames() and reuse rather than duplicate. HANDLE data = ::GetClipboardData( - ClipboardFormatType::GetCFHDropType().ToFormatEtc().cfFormat); + ClipboardFormatType::CFHDropType().ToFormatEtc().cfFormat); if (data) { { base::win::ScopedHGlobal<HDROP> hdrop(data); @@ -583,7 +608,7 @@ void ClipboardWin::ReadFilenames(ClipboardBuffer buffer, } data = ::GetClipboardData( - ClipboardFormatType::GetFilenameType().ToFormatEtc().cfFormat); + ClipboardFormatType::FilenameType().ToFormatEtc().cfFormat); if (data) { { // filename using Unicode @@ -597,7 +622,7 @@ void ClipboardWin::ReadFilenames(ClipboardBuffer buffer, } data = ::GetClipboardData( - ClipboardFormatType::GetFilenameAType().ToFormatEtc().cfFormat); + ClipboardFormatType::FilenameAType().ToFormatEtc().cfFormat); if (data) { { // filename using ASCII @@ -627,8 +652,8 @@ void ClipboardWin::ReadBookmark(const DataTransferEndpoint* data_dst, if (!clipboard.Acquire(GetClipboardWindow())) return; - HANDLE data = ::GetClipboardData( - ClipboardFormatType::GetUrlType().ToFormatEtc().cfFormat); + HANDLE data = + ::GetClipboardData(ClipboardFormatType::UrlType().ToFormatEtc().cfFormat); if (!data) return; @@ -666,37 +691,19 @@ void ClipboardWin::ReadData(const ClipboardFormatType& format, // |data_src| is not used. It's only passed to be consistent with other // platforms. -void ClipboardWin::WritePortableRepresentations( +void ClipboardWin::WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) { - DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - - ScopedClipboard clipboard; - if (!clipboard.Acquire(GetClipboardWindow())) - return; - - ::EmptyClipboard(); - - for (const auto& object : objects) - DispatchPortableRepresentation(object.first, object.second); -} - -// |data_src| is not used. It's only passed to be consistent with other -// platforms. -void ClipboardWin::WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) { - DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - ScopedClipboard clipboard; if (!clipboard.Acquire(GetClipboardWindow())) return; - ::EmptyClipboard(); DispatchPlatformRepresentations(std::move(platform_representations)); + for (const auto& object : objects) + DispatchPortableRepresentation(object.first, object.second); } void ClipboardWin::WriteText(const char* text_data, size_t text_len) { @@ -704,7 +711,7 @@ void ClipboardWin::WriteText(const char* text_data, size_t text_len) { base::UTF8ToUTF16(text_data, text_len, &text); HGLOBAL glob = CreateGlobalData(text); - WriteToClipboard(ClipboardFormatType::GetPlainTextType(), glob); + WriteToClipboard(ClipboardFormatType::PlainTextType(), glob); } void ClipboardWin::WriteHTML(const char* markup_data, @@ -720,7 +727,7 @@ void ClipboardWin::WriteHTML(const char* markup_data, std::string html_fragment = ClipboardUtil::HtmlToCFHtml(markup, url); HGLOBAL glob = CreateGlobalData(html_fragment); - WriteToClipboard(ClipboardFormatType::GetHtmlType(), glob); + WriteToClipboard(ClipboardFormatType::HtmlType(), glob); } void ClipboardWin::WriteSvg(const char* markup_data, size_t markup_len) { @@ -728,18 +735,18 @@ void ClipboardWin::WriteSvg(const char* markup_data, size_t markup_len) { base::UTF8ToUTF16(markup_data, markup_len, &markup); HGLOBAL glob = CreateGlobalData(markup); - WriteToClipboard(ClipboardFormatType::GetSvgType(), glob); + WriteToClipboard(ClipboardFormatType::SvgType(), glob); } void ClipboardWin::WriteRTF(const char* rtf_data, size_t data_len) { - WriteData(ClipboardFormatType::GetRtfType(), rtf_data, data_len); + WriteData(ClipboardFormatType::RtfType(), rtf_data, data_len); } void ClipboardWin::WriteFilenames(std::vector<ui::FileInfo> filenames) { STGMEDIUM storage = ClipboardUtil::CreateStorageForFileNames(filenames); if (storage.tymed == TYMED_NULL) return; - WriteToClipboard(ClipboardFormatType::GetCFHDropType(), storage.hGlobal); + WriteToClipboard(ClipboardFormatType::CFHDropType(), storage.hGlobal); } void ClipboardWin::WriteBookmark(const char* title_data, @@ -753,13 +760,13 @@ void ClipboardWin::WriteBookmark(const char* title_data, std::u16string wide_bookmark = base::UTF8ToUTF16(bookmark); HGLOBAL glob = CreateGlobalData(wide_bookmark); - WriteToClipboard(ClipboardFormatType::GetUrlType(), glob); + WriteToClipboard(ClipboardFormatType::UrlType(), glob); } void ClipboardWin::WriteWebSmartPaste() { DCHECK_NE(clipboard_owner_->hwnd(), nullptr); ::SetClipboardData( - ClipboardFormatType::GetWebKitSmartPasteType().ToFormatEtc().cfFormat, + ClipboardFormatType::WebKitSmartPasteType().ToFormatEtc().cfFormat, nullptr); } @@ -779,11 +786,11 @@ void ClipboardWin::WriteBitmap(const SkBitmap& bitmap) { &png_encoded_bitmap)) { HGLOBAL png_hglobal = skia::CreateHGlobalForByteArray(png_encoded_bitmap); if (png_hglobal) - WriteToClipboard(ClipboardFormatType::GetPngType(), png_hglobal); + WriteToClipboard(ClipboardFormatType::PngType(), png_hglobal); } HGLOBAL dibv5_hglobal = skia::CreateDIBV5ImageDataFromN32SkBitmap(bitmap); if (dibv5_hglobal) - WriteToClipboard(ClipboardFormatType::GetBitmapType(), dibv5_hglobal); + WriteToClipboard(ClipboardFormatType::BitmapType(), dibv5_hglobal); } void ClipboardWin::WriteData(const ClipboardFormatType& format, @@ -799,6 +806,26 @@ void ClipboardWin::WriteData(const ClipboardFormatType& format, WriteToClipboard(format, hdata); } +std::vector<uint8_t> ClipboardWin::ReadPngInternal( + ClipboardBuffer buffer) const { + DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); + + // Acquire the clipboard. + ScopedClipboard clipboard; + if (!clipboard.Acquire(GetClipboardWindow())) + return std::vector<uint8_t>(); + + HANDLE data = + ::GetClipboardData(ClipboardFormatType::PngType().ToFormatEtc().cfFormat); + + if (!data) + return std::vector<uint8_t>(); + + std::string result(static_cast<const char*>(::GlobalLock(data)), + ::GlobalSize(data)); + ::GlobalUnlock(data); + return std::vector<uint8_t>(result.begin(), result.end()); +} SkBitmap ClipboardWin::ReadImageInternal(ClipboardBuffer buffer) const { DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); @@ -816,6 +843,31 @@ SkBitmap ClipboardWin::ReadImageInternal(ClipboardBuffer buffer) const { return SkBitmap(); int color_table_length = 0; + // Image is too large, and may cause an allocation failure. + // See https://crbug.com/1164680. + constexpr size_t kMaxImageSizeBytes = 1 << 27; // 128 MiB + size_t image_size_bytes; + // Estimate the number of bytes per pixel. For images with fewer than one byte + // pixel we will over-estimate the size. For compressed images we will + // over-estimate the size, but some overestimating of the storage size is okay + // and the calculation will be a good estimate of the decompressed size. The + // reported bug was for uncompressed 32-bit images where this code will give + // correct results. + size_t bytes_per_pixel = bitmap->bmiHeader.biBitCount / 8; + if (bytes_per_pixel == 0) + bytes_per_pixel = 1; + // Calculate the size of the bitmap. This is not an exact calculation but that + // doesn't matter for this purpose. If the calculation overflows then the + // image is too big. Return an empty image. + if (!base::CheckMul( + bitmap->bmiHeader.biWidth, + base::CheckMul(bitmap->bmiHeader.biHeight, bytes_per_pixel)) + .AssignIfValid(&image_size_bytes)) + return SkBitmap(); + // If the image size is too big then return an empty image. + if (image_size_bytes > kMaxImageSizeBytes) + return SkBitmap(); + // For more information on BITMAPINFOHEADER and biBitCount definition, // see https://docs.microsoft.com/en-us/windows/win32/wmdm/-bitmapinfoheader switch (bitmap->bmiHeader.biBitCount) { diff --git a/chromium/ui/base/clipboard/clipboard_win.h b/chromium/ui/base/clipboard/clipboard_win.h index 33b6abca99d..355190e6c1f 100644 --- a/chromium/ui/base/clipboard/clipboard_win.h +++ b/chromium/ui/base/clipboard/clipboard_win.h @@ -10,7 +10,6 @@ #include <memory> -#include "base/macros.h" #include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/clipboard_format_type.h" @@ -22,7 +21,14 @@ class MessageWindow; namespace ui { +// Documentation on the underlying Win32 API this ultimately abstracts is +// available at +// https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard. class ClipboardWin : public Clipboard { + public: + ClipboardWin(const ClipboardWin&) = delete; + ClipboardWin& operator=(const ClipboardWin&) = delete; + private: friend class Clipboard; @@ -32,7 +38,8 @@ class ClipboardWin : public Clipboard { // Clipboard overrides: void OnPreShutdown() override; DataTransferEndpoint* GetSource(ClipboardBuffer buffer) const override; - uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override; + const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const override; bool IsFormatAvailable(const ClipboardFormatType& format, ClipboardBuffer buffer, const DataTransferEndpoint* data_dst) const override; @@ -80,12 +87,9 @@ class ClipboardWin : public Clipboard { void ReadData(const ClipboardFormatType& format, const DataTransferEndpoint* data_dst, std::string* result) const override; - void WritePortableRepresentations( + void WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) override; - void WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) override; void WriteText(const char* text_data, size_t text_len) override; @@ -105,6 +109,7 @@ class ClipboardWin : public Clipboard { void WriteData(const ClipboardFormatType& format, const char* data_data, size_t data_len) override; + std::vector<uint8_t> ReadPngInternal(ClipboardBuffer buffer) const; SkBitmap ReadImageInternal(ClipboardBuffer buffer) const; // Safely write to system clipboard. Free |handle| on failure. @@ -118,7 +123,11 @@ class ClipboardWin : public Clipboard { // Mark this as mutable so const methods can still do lazy initialization. mutable std::unique_ptr<base::win::MessageWindow> clipboard_owner_; - DISALLOW_COPY_AND_ASSIGN(ClipboardWin); + // Mapping of OS-provided sequence number to a unique token. + mutable struct { + DWORD sequence_number; + ClipboardSequenceNumberToken token; + } clipboard_sequence_; }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_x11.cc b/chromium/ui/base/clipboard/clipboard_x11.cc index 2807d6e8ddc..304223c9d8e 100644 --- a/chromium/ui/base/clipboard/clipboard_x11.cc +++ b/chromium/ui/base/clipboard/clipboard_x11.cc @@ -8,6 +8,7 @@ #include <limits> #include <memory> #include <string> +#include <vector> #include "base/bind.h" #include "base/containers/contains.h" @@ -50,7 +51,8 @@ DataTransferEndpoint* ClipboardX11::GetSource(ClipboardBuffer buffer) const { return it == data_src_.end() ? nullptr : it->second.get(); } -uint64_t ClipboardX11::GetSequenceNumber(ClipboardBuffer buffer) const { +const ClipboardSequenceNumberToken& ClipboardX11::GetSequenceNumber( + ClipboardBuffer buffer) const { DCHECK(CalledOnValidThread()); return buffer == ClipboardBuffer::kCopyPaste ? clipboard_sequence_number_ : primary_sequence_number_; @@ -88,10 +90,10 @@ void ClipboardX11::ReadAvailableTypes( for (const auto& mime_type : available_types) { // Special handling for chromium/x-web-custom-data. We must read the data // and deserialize it to find the list of mime types to report. - if (mime_type == ClipboardFormatType::GetWebCustomDataType().GetName()) { + if (mime_type == ClipboardFormatType::WebCustomDataType().GetName()) { auto data(x_clipboard_helper_->Read( buffer, x_clipboard_helper_->GetAtomsForFormat( - ClipboardFormatType::GetWebCustomDataType()))); + ClipboardFormatType::WebCustomDataType()))); if (data.IsValid()) ReadCustomDataTypes(data.GetData(), data.GetSize(), types); } else { @@ -162,8 +164,8 @@ void ClipboardX11::ReadHTML(ClipboardBuffer buffer, *fragment_end = 0; SelectionData data(x_clipboard_helper_->Read( - buffer, x_clipboard_helper_->GetAtomsForFormat( - ClipboardFormatType::GetHtmlType()))); + buffer, + x_clipboard_helper_->GetAtomsForFormat(ClipboardFormatType::HtmlType()))); if (data.IsValid()) { *markup = data.GetHtml(); @@ -182,8 +184,8 @@ void ClipboardX11::ReadSvg(ClipboardBuffer buffer, RecordRead(ClipboardFormatMetric::kSvg); SelectionData data(x_clipboard_helper_->Read( - buffer, x_clipboard_helper_->GetAtomsForFormat( - ClipboardFormatType::GetSvgType()))); + buffer, + x_clipboard_helper_->GetAtomsForFormat(ClipboardFormatType::SvgType()))); if (data.IsValid()) { std::string markup; data.AssignTo(&markup); @@ -200,8 +202,8 @@ void ClipboardX11::ReadRTF(ClipboardBuffer buffer, RecordRead(ClipboardFormatMetric::kRtf); SelectionData data(x_clipboard_helper_->Read( - buffer, x_clipboard_helper_->GetAtomsForFormat( - ClipboardFormatType::GetRtfType()))); + buffer, + x_clipboard_helper_->GetAtomsForFormat(ClipboardFormatType::RtfType()))); if (data.IsValid()) data.AssignTo(result); } @@ -211,8 +213,9 @@ void ClipboardX11::ReadRTF(ClipboardBuffer buffer, void ClipboardX11::ReadPng(ClipboardBuffer buffer, const DataTransferEndpoint* data_dst, ReadPngCallback callback) const { - // TODO(crbug.com/1201018): Implement this. - NOTIMPLEMENTED(); + DCHECK(IsSupportedClipboardBuffer(buffer)); + RecordRead(ClipboardFormatMetric::kPng); + std::move(callback).Run(ReadPngInternal(buffer)); } // |data_dst| is not used. It's only passed to be consistent with other @@ -222,7 +225,10 @@ void ClipboardX11::ReadImage(ClipboardBuffer buffer, ReadImageCallback callback) const { DCHECK(IsSupportedClipboardBuffer(buffer)); RecordRead(ClipboardFormatMetric::kImage); - std::move(callback).Run(ReadImageInternal(buffer)); + auto png_data = ReadPngInternal(buffer); + SkBitmap bitmap; + gfx::PNGCodec::Decode(png_data.data(), png_data.size(), &bitmap); + std::move(callback).Run(bitmap); } // |data_dst| is not used. It's only passed to be consistent with other @@ -236,7 +242,7 @@ void ClipboardX11::ReadCustomData(ClipboardBuffer buffer, SelectionData data(x_clipboard_helper_->Read( buffer, x_clipboard_helper_->GetAtomsForFormat( - ClipboardFormatType::GetWebCustomDataType()))); + ClipboardFormatType::WebCustomDataType()))); if (data.IsValid()) ReadCustomDataForType(data.GetData(), data.GetSize(), type, result); } @@ -251,7 +257,7 @@ void ClipboardX11::ReadFilenames(ClipboardBuffer buffer, SelectionData data(x_clipboard_helper_->Read( buffer, x_clipboard_helper_->GetAtomsForFormat( - ClipboardFormatType::GetFilenamesType()))); + ClipboardFormatType::FilenamesType()))); std::string uri_list; if (data.IsValid()) data.AssignTo(&uri_list); @@ -289,20 +295,8 @@ bool ClipboardX11::IsSelectionBufferAvailable() const { } #endif // defined(USE_OZONE) -// |data_src| is not used. It's only passed to be consistent with other -// platforms. -void ClipboardX11::WritePortableRepresentations( - ClipboardBuffer buffer, - const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) { - DCHECK(CalledOnValidThread()); - DCHECK(IsSupportedClipboardBuffer(buffer)); - - x_clipboard_helper_->CreateNewClipboardData(); - for (const auto& object : objects) - DispatchPortableRepresentation(object.first, object.second); - x_clipboard_helper_->TakeOwnershipOfSelection(buffer); - +void ClipboardX11::WritePortableTextRepresentation(ClipboardBuffer buffer, + const ObjectMap& objects) { if (buffer == ClipboardBuffer::kCopyPaste) { auto text_iter = objects.find(PortableFormat::kText); if (text_iter != objects.end()) { @@ -317,14 +311,13 @@ void ClipboardX11::WritePortableRepresentations( ClipboardBuffer::kSelection); } } - - data_src_[buffer] = std::move(data_src); } // |data_src| is not used. It's only passed to be consistent with other // platforms. -void ClipboardX11::WritePlatformRepresentations( +void ClipboardX11::WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, + const ObjectMap& objects, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) { DCHECK(CalledOnValidThread()); @@ -332,7 +325,12 @@ void ClipboardX11::WritePlatformRepresentations( x_clipboard_helper_->CreateNewClipboardData(); DispatchPlatformRepresentations(std::move(platform_representations)); + for (const auto& object : objects) + DispatchPortableRepresentation(object.first, object.second); x_clipboard_helper_->TakeOwnershipOfSelection(buffer); + + WritePortableTextRepresentation(buffer, objects); + data_src_[buffer] = std::move(data_src); } @@ -374,7 +372,7 @@ void ClipboardX11::WriteSvg(const char* markup_data, size_t markup_len) { } void ClipboardX11::WriteRTF(const char* rtf_data, size_t data_len) { - WriteData(ClipboardFormatType::GetRtfType(), rtf_data, data_len); + WriteData(ClipboardFormatType::RtfType(), rtf_data, data_len); } void ClipboardX11::WriteFilenames(std::vector<ui::FileInfo> filenames) { @@ -382,7 +380,7 @@ void ClipboardX11::WriteFilenames(std::vector<ui::FileInfo> filenames) { scoped_refptr<base::RefCountedMemory> mem( base::RefCountedString::TakeString(&uri_list)); x_clipboard_helper_->InsertMapping( - ClipboardFormatType::GetFilenamesType().GetName(), mem); + ClipboardFormatType::FilenamesType().GetName(), mem); } void ClipboardX11::WriteBookmark(const char* title_data, @@ -431,29 +429,30 @@ void ClipboardX11::WriteData(const ClipboardFormatType& format, x_clipboard_helper_->InsertMapping(format.GetName(), mem); } -SkBitmap ClipboardX11::ReadImageInternal(ClipboardBuffer buffer) const { +std::vector<uint8_t> ClipboardX11::ReadPngInternal( + ClipboardBuffer buffer) const { DCHECK(CalledOnValidThread()); - // TODO(https://crbug.com/443355): Since now that ReadImage() is async, - // refactor the code to keep a callback with the request, and invoke the - // callback when the request is satisfied. + // TODO(https://crbug.com/443355): Since ReadPng() is async, refactor the code + // to keep a callback with the request, and invoke the callback when the + // request is satisfied. SelectionData data(x_clipboard_helper_->Read( - buffer, x_clipboard_helper_->GetAtomsForFormat( - ClipboardFormatType::GetBitmapType()))); + buffer, + x_clipboard_helper_->GetAtomsForFormat(ClipboardFormatType::PngType()))); + if (data.IsValid()) { - SkBitmap bitmap; - if (gfx::PNGCodec::Decode(data.GetData(), data.GetSize(), &bitmap)) - return SkBitmap(bitmap); + return std::vector<uint8_t>(data.GetData(), + data.GetData() + data.GetSize()); } - return SkBitmap(); + return std::vector<uint8_t>(); } void ClipboardX11::OnSelectionChanged(ClipboardBuffer buffer) { if (buffer == ClipboardBuffer::kCopyPaste) - clipboard_sequence_number_++; + clipboard_sequence_number_ = ClipboardSequenceNumberToken(); else - primary_sequence_number_++; + primary_sequence_number_ = ClipboardSequenceNumberToken(); ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged(); } diff --git a/chromium/ui/base/clipboard/clipboard_x11.h b/chromium/ui/base/clipboard/clipboard_x11.h index 88a5ed96273..be7eb96c88e 100644 --- a/chromium/ui/base/clipboard/clipboard_x11.h +++ b/chromium/ui/base/clipboard/clipboard_x11.h @@ -8,7 +8,6 @@ #include <cstdint> #include <memory> -#include "base/macros.h" #include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/clipboard_buffer.h" @@ -17,6 +16,10 @@ namespace ui { class XClipboardHelper; class ClipboardX11 : public Clipboard { + public: + ClipboardX11(const ClipboardX11&) = delete; + ClipboardX11& operator=(const ClipboardX11&) = delete; + private: friend class Clipboard; @@ -26,7 +29,8 @@ class ClipboardX11 : public Clipboard { // Clipboard overrides: void OnPreShutdown() override; DataTransferEndpoint* GetSource(ClipboardBuffer buffer) const override; - uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override; + const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const override; bool IsFormatAvailable(const ClipboardFormatType& format, ClipboardBuffer buffer, const DataTransferEndpoint* data_dst) const override; @@ -77,12 +81,11 @@ class ClipboardX11 : public Clipboard { #if defined(USE_OZONE) bool IsSelectionBufferAvailable() const override; #endif // defined(USE_OZONE) - void WritePortableRepresentations( + void WritePortableTextRepresentation(ClipboardBuffer buffer, + const ObjectMap& objects); + void WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) override; - void WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) override; void WriteText(const char* text_data, size_t text_len) override; @@ -103,18 +106,16 @@ class ClipboardX11 : public Clipboard { const char* data_data, size_t data_len) override; - SkBitmap ReadImageInternal(ClipboardBuffer buffer) const; + std::vector<uint8_t> ReadPngInternal(ClipboardBuffer buffer) const; void OnSelectionChanged(ClipboardBuffer buffer); std::unique_ptr<XClipboardHelper> x_clipboard_helper_; - uint64_t clipboard_sequence_number_ = 0; - uint64_t primary_sequence_number_ = 0; + ClipboardSequenceNumberToken clipboard_sequence_number_; + ClipboardSequenceNumberToken primary_sequence_number_; base::flat_map<ClipboardBuffer, std::unique_ptr<DataTransferEndpoint>> data_src_; - - DISALLOW_COPY_AND_ASSIGN(ClipboardX11); }; } // namespace ui diff --git a/chromium/ui/base/clipboard/scoped_clipboard_writer.cc b/chromium/ui/base/clipboard/scoped_clipboard_writer.cc index 4e2394e4116..153f169d2cd 100644 --- a/chromium/ui/base/clipboard/scoped_clipboard_writer.cc +++ b/chromium/ui/base/clipboard/scoped_clipboard_writer.cc @@ -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" @@ -24,16 +26,24 @@ ScopedClipboardWriter::ScopedClipboardWriter( ScopedClipboardWriter::~ScopedClipboardWriter() { static constexpr size_t kMaxRepresentations = 1 << 12; - DCHECK(objects_.empty() || platform_representations_.empty()) - << "Portable and Platform representations should not be written on the " - "same write."; DCHECK(platform_representations_.size() < kMaxRepresentations); - if (!objects_.empty()) { - Clipboard::GetForCurrentThread()->WritePortableRepresentations( - buffer_, objects_, std::move(data_src_)); - } else if (!platform_representations_.empty()) { - Clipboard::GetForCurrentThread()->WritePlatformRepresentations( - buffer_, std::move(platform_representations_), std::move(data_src_)); + // 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_), + std::move(data_src_)); } if (confidential_) @@ -176,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::UTF16ToUTF8(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 diff --git a/chromium/ui/base/clipboard/scoped_clipboard_writer.h b/chromium/ui/base/clipboard/scoped_clipboard_writer.h index 4d5122026dd..879acd4f6f0 100644 --- a/chromium/ui/base/clipboard/scoped_clipboard_writer.h +++ b/chromium/ui/base/clipboard/scoped_clipboard_writer.h @@ -8,7 +8,7 @@ #include <string> #include "base/component_export.h" -#include "base/macros.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" @@ -36,7 +36,8 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ScopedClipboardWriter { explicit ScopedClipboardWriter( ClipboardBuffer buffer, std::unique_ptr<DataTransferEndpoint> src = nullptr); - + ScopedClipboardWriter(const ScopedClipboardWriter&) = delete; + ScopedClipboardWriter& operator=(const ScopedClipboardWriter&) = delete; ~ScopedClipboardWriter(); // Converts |text| to UTF-8 and adds it to the clipboard. @@ -76,6 +77,7 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ScopedClipboardWriter { // Data is written to the system clipboard in the same order as WriteData // calls are received. + // This is only used to write custom format data. void WriteData(const std::u16string& format, mojo_base::BigBuffer data); void WriteImage(const SkBitmap& bitmap); @@ -93,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_; @@ -105,8 +110,6 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ScopedClipboardWriter { // not set, or the source of the data can't be represented by // DataTransferEndpoint. std::unique_ptr<DataTransferEndpoint> data_src_; - - DISALLOW_COPY_AND_ASSIGN(ScopedClipboardWriter); }; } // namespace ui |