From e163bd71025161041ce35d6b6641ecf3d102bd31 Mon Sep 17 00:00:00 2001 From: erg Date: Tue, 7 Jun 2016 16:25:34 -0700 Subject: [PATCH] Reland "Implement ui::ClipboardMus." Should fix size_t conversion issues. Original issue's description: > Implement ui::ClipboardMus. > > This lets chrome in mash read and write from the mus server's clipboard. > > BUG=581460 > > Committed: https://crrev.com/bbc764eb7a7c1937d26f43f6087604ebc2d048ad > Cr-Commit-Position: refs/heads/master@{#398351} TBR=dcheng@chromium.org,brettw@chromium.org,sky@chromium.org BUG=581460 Review-Url: https://codereview.chromium.org/2044973002 Cr-Commit-Position: refs/heads/master@{#398413} --- base/threading/thread_restrictions.h | 5 + components/mus/clipboard/BUILD.gn | 1 + components/mus/clipboard/clipboard_impl.cc | 16 +- components/mus/clipboard/clipboard_impl.h | 8 +- .../mus/clipboard/clipboard_unittest.cc | 31 +- .../mus/public/interfaces/clipboard.mojom | 22 +- mojo/common/common_type_converters.cc | 8 + mojo/common/common_type_converters.h | 5 + ui/base/BUILD.gn | 1 - ui/base/clipboard/clipboard.cc | 60 ++-- ui/base/clipboard/clipboard.h | 20 +- ui/base/clipboard/clipboard_android.cc | 2 - ui/base/clipboard/clipboard_aura.cc | 8 +- ui/base/clipboard/clipboard_aurax11.cc | 34 +- ui/base/clipboard/clipboard_constants.cc | 6 + ui/base/clipboard/clipboard_test_template.h | 19 +- ui/base/clipboard/clipboard_unittest.cc | 12 + ui/base/clipboard/custom_data_helper.h | 2 - ui/base/clipboard/custom_data_helper_linux.cc | 13 - ui/base/clipboard/custom_data_helper_mac.mm | 4 +- .../os_exchange_data_provider_aurax11.cc | 31 +- ui/base/test/test_clipboard.cc | 3 +- ui/base/test/test_clipboard_unittest.cc | 12 + ui/base/ui_base.gyp | 1 - ui/base/x/selection_utils.cc | 3 +- ui/base/x/selection_utils.h | 1 - ui/views/mus/BUILD.gn | 6 + ui/views/mus/DEPS | 1 + ui/views/mus/clipboard_mus.cc | 338 ++++++++++++++++++ ui/views/mus/clipboard_mus.h | 87 +++++ ui/views/mus/clipboard_unittest.cc | 145 ++++++++ ui/views/mus/window_manager_connection.cc | 6 + ui/views/mus/window_manager_connection.h | 2 + 33 files changed, 783 insertions(+), 130 deletions(-) delete mode 100644 ui/base/clipboard/custom_data_helper_linux.cc create mode 100644 ui/views/mus/clipboard_mus.cc create mode 100644 ui/views/mus/clipboard_mus.h create mode 100644 ui/views/mus/clipboard_unittest.cc diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h index 8d937b7468d806..e916ecc6f3b954 100644 --- a/base/threading/thread_restrictions.h +++ b/base/threading/thread_restrictions.h @@ -82,6 +82,7 @@ class WindowResizeHelperMac; } namespace views { +class ClipboardMus; class ScreenMus; } @@ -235,6 +236,10 @@ class BASE_EXPORT ThreadRestrictions { #if !defined(OFFICIAL_BUILD) friend class content::SoftwareOutputDeviceMus; // Interim non-production code #endif + // In the non-mus case, we called blocking OS functions in the ui::Clipboard + // implementation which weren't caught by threading restrictions. Our + // blocking calls to mus, however, are. + friend class views::ClipboardMus; friend class views::ScreenMus; // END USAGE THAT NEEDS TO BE FIXED. diff --git a/components/mus/clipboard/BUILD.gn b/components/mus/clipboard/BUILD.gn index af5cdd43db1cd7..f6e26613b45836 100644 --- a/components/mus/clipboard/BUILD.gn +++ b/components/mus/clipboard/BUILD.gn @@ -15,6 +15,7 @@ source_set("lib") { deps = [ "//base", "//components/mus/public/interfaces", + "//mojo/common", "//mojo/public/cpp/bindings", "//mojo/public/cpp/bindings:callback", "//services/shell/public/cpp", diff --git a/components/mus/clipboard/clipboard_impl.cc b/components/mus/clipboard/clipboard_impl.cc index 0a66431592ab5d..221359f963511f 100644 --- a/components/mus/clipboard/clipboard_impl.cc +++ b/components/mus/clipboard/clipboard_impl.cc @@ -8,6 +8,7 @@ #include #include "base/macros.h" +#include "mojo/common/common_type_converters.h" #include "mojo/public/cpp/bindings/array.h" #include "mojo/public/cpp/bindings/callback.h" #include "mojo/public/cpp/bindings/string.h" @@ -84,20 +85,15 @@ void ClipboardImpl::GetAvailableMimeTypes( clipboard_state_[clipboard_num]->GetMimeTypes()); } -void ClipboardImpl::ReadMimeType( - uint64_t sequence, +void ClipboardImpl::ReadClipboardData( Clipboard::Type clipboard_type, const String& mime_type, - const ReadMimeTypeCallback& callback) { + const ReadClipboardDataCallback& callback) { int clipboard_num = static_cast(clipboard_type); Array mime_data(nullptr); - bool valid_sequence_number = - clipboard_state_[clipboard_num]->sequence_number() == sequence; - - if (valid_sequence_number) - clipboard_state_[clipboard_num]->GetData(mime_type, &mime_data); - - callback.Run(valid_sequence_number, std::move(mime_data)); + uint64_t sequence = clipboard_state_[clipboard_num]->sequence_number(); + clipboard_state_[clipboard_num]->GetData(mime_type, &mime_data); + callback.Run(sequence, std::move(mime_data)); } void ClipboardImpl::WriteClipboardData( diff --git a/components/mus/clipboard/clipboard_impl.h b/components/mus/clipboard/clipboard_impl.h index f77977cad4a74e..7f62f1c1df5a8c 100644 --- a/components/mus/clipboard/clipboard_impl.h +++ b/components/mus/clipboard/clipboard_impl.h @@ -40,11 +40,9 @@ class ClipboardImpl : public mojom::Clipboard { void GetAvailableMimeTypes( mojom::Clipboard::Type clipboard_types, const GetAvailableMimeTypesCallback& callback) override; - void ReadMimeType( - uint64_t sequence, - mojom::Clipboard::Type clipboard_type, - const mojo::String& mime_type, - const ReadMimeTypeCallback& callback) override; + void ReadClipboardData(mojom::Clipboard::Type clipboard_type, + const mojo::String& mime_type, + const ReadClipboardDataCallback& callback) override; void WriteClipboardData( mojom::Clipboard::Type clipboard_type, mojo::Map> data, diff --git a/components/mus/clipboard/clipboard_unittest.cc b/components/mus/clipboard/clipboard_unittest.cc index d9b5ee5dadf746..764618395c7499 100644 --- a/components/mus/clipboard/clipboard_unittest.cc +++ b/components/mus/clipboard/clipboard_unittest.cc @@ -58,14 +58,13 @@ class ClipboardAppTest : public shell::test::ShellTest { return types.To>(); } - bool GetDataOfType(uint64_t sequence_number, const std::string& mime_type, - std::string* data) { + bool GetDataOfType(const std::string& mime_type, std::string* data) { bool valid = false; Array raw_data; - clipboard_->ReadMimeType( - sequence_number, Clipboard::Type::COPY_PASTE, mime_type, - &valid, &raw_data); - valid = valid && !raw_data.is_null(); + uint64_t sequence_number = 0; + clipboard_->ReadClipboardData(Clipboard::Type::COPY_PASTE, mime_type, + &sequence_number, &raw_data); + valid = !raw_data.is_null(); *data = raw_data.is_null() ? "" : raw_data.To(); return valid; } @@ -73,7 +72,7 @@ class ClipboardAppTest : public shell::test::ShellTest { void SetStringText(const std::string& data) { uint64_t sequence_number; Map> mime_data; - mime_data[Clipboard::MIME_TYPE_TEXT] = Array::From(data); + mime_data[mojom::kMimeTypeText] = Array::From(data); clipboard_->WriteClipboardData(Clipboard::Type::COPY_PASTE, std::move(mime_data), &sequence_number); @@ -89,26 +88,26 @@ TEST_F(ClipboardAppTest, EmptyClipboardOK) { EXPECT_EQ(0ul, GetSequenceNumber()); EXPECT_TRUE(GetAvailableFormatMimeTypes().empty()); std::string data; - EXPECT_FALSE(GetDataOfType(0ul, Clipboard::MIME_TYPE_TEXT, &data)); + EXPECT_FALSE(GetDataOfType(mojom::kMimeTypeText, &data)); } TEST_F(ClipboardAppTest, CanReadBackText) { std::string data; EXPECT_EQ(0ul, GetSequenceNumber()); - EXPECT_FALSE(GetDataOfType(0ul, Clipboard::MIME_TYPE_TEXT, &data)); + EXPECT_FALSE(GetDataOfType(mojom::kMimeTypeText, &data)); SetStringText(kPlainTextData); EXPECT_EQ(1ul, GetSequenceNumber()); - EXPECT_TRUE(GetDataOfType(1ul, Clipboard::MIME_TYPE_TEXT, &data)); + EXPECT_TRUE(GetDataOfType(mojom::kMimeTypeText, &data)); EXPECT_EQ(kPlainTextData, data); } TEST_F(ClipboardAppTest, CanSetMultipleDataTypesAtOnce) { Map> mime_data; - mime_data[Clipboard::MIME_TYPE_TEXT] = + mime_data[mojom::kMimeTypeText] = Array::From(std::string(kPlainTextData)); - mime_data[Clipboard::MIME_TYPE_HTML] = + mime_data[mojom::kMimeTypeHTML] = Array::From(std::string(kHtmlData)); uint64_t sequence_num = 0; @@ -118,9 +117,9 @@ TEST_F(ClipboardAppTest, CanSetMultipleDataTypesAtOnce) { EXPECT_EQ(1ul, sequence_num); std::string data; - EXPECT_TRUE(GetDataOfType(1ul, Clipboard::MIME_TYPE_TEXT, &data)); + EXPECT_TRUE(GetDataOfType(mojom::kMimeTypeText, &data)); EXPECT_EQ(kPlainTextData, data); - EXPECT_TRUE(GetDataOfType(1ul, Clipboard::MIME_TYPE_HTML, &data)); + EXPECT_TRUE(GetDataOfType(mojom::kMimeTypeHTML, &data)); EXPECT_EQ(kHtmlData, data); } @@ -129,7 +128,7 @@ TEST_F(ClipboardAppTest, CanClearClipboardWithZeroArray) { SetStringText(kPlainTextData); EXPECT_EQ(1ul, GetSequenceNumber()); - EXPECT_TRUE(GetDataOfType(1ul, Clipboard::MIME_TYPE_TEXT, &data)); + EXPECT_TRUE(GetDataOfType(mojom::kMimeTypeText, &data)); EXPECT_EQ(kPlainTextData, data); Map> mime_data; @@ -139,7 +138,7 @@ TEST_F(ClipboardAppTest, CanClearClipboardWithZeroArray) { &sequence_num); EXPECT_EQ(2ul, sequence_num); - EXPECT_FALSE(GetDataOfType(2ul, Clipboard::MIME_TYPE_TEXT, &data)); + EXPECT_FALSE(GetDataOfType(mojom::kMimeTypeText, &data)); } } // namespace clipboard diff --git a/components/mus/public/interfaces/clipboard.mojom b/components/mus/public/interfaces/clipboard.mojom index 0e7c67581a94e5..201a7c3dea5ecb 100644 --- a/components/mus/public/interfaces/clipboard.mojom +++ b/components/mus/public/interfaces/clipboard.mojom @@ -4,6 +4,14 @@ module mus.mojom; +const string kMimeTypeHTML = "text/html"; +const string kMimeTypeMozillaURL = "text/x-moz-url"; +const string kMimeTypePNG = "image/png"; +const string kMimeTypeRTF = "text/rtf"; +const string kMimeTypeText = "text/plain"; +const string kMimeTypeURIList = "text/uri-list"; +const string kMimeTypeURL = "text/url"; + interface Clipboard { enum Type { COPY_PASTE = 0, @@ -11,11 +19,6 @@ interface Clipboard { DRAG = 2 }; - // Mime type constants - const string MIME_TYPE_TEXT = "text/plain"; - const string MIME_TYPE_HTML = "text/html"; - const string MIME_TYPE_URL = "text/url"; - // Returns a sequence number which uniquely identifies clipboard state. // Clients are able to assume that the clipboard contents are unchanged as // long as this number has not changed. This number is monotonically @@ -29,17 +32,14 @@ interface Clipboard { GetAvailableMimeTypes(Type clipboard_types) => (uint64 sequence, array types); - // If the current sequence number is still |sequence|, returns true and the - // current data associated with the requested Mime type. When the sequence - // number has changed (because new data has been written to the clipboard), - // returns false and null for |data|. + // Returns the current data associated with the requested Mime type. // // We don't want to provide one API to return the entire clipboard state // because the combined size of the clipboard can be megabytes, especially // when image data is involved. [Sync] - ReadMimeType(uint64 sequence, Type clipboard_type, string mime_type) - => (bool sequence_valid, array? data); + ReadClipboardData(Type clipboard_type, string mime_type) + => (uint64 sequence, array? data); // Writes a set of mime types to the clipboard. This will increment the // sequence number and return that. In the case of an empty or null map, diff --git a/mojo/common/common_type_converters.cc b/mojo/common/common_type_converters.cc index 78418f8e41a1b7..92ae3e2682034a 100644 --- a/mojo/common/common_type_converters.cc +++ b/mojo/common/common_type_converters.cc @@ -56,6 +56,14 @@ Array TypeConverter, std::string>::Convert( return result; } +Array TypeConverter, base::StringPiece>::Convert( + const base::StringPiece& input) { + Array result(input.size()); + if (!input.empty()) + memcpy(&result.front(), input.data(), input.size()); + return result; +} + base::string16 TypeConverter>::Convert( const Array& input) { if (input.is_null() || input.empty()) diff --git a/mojo/common/common_type_converters.h b/mojo/common/common_type_converters.h index 6dc31f16ad41f2..a065f050791d71 100644 --- a/mojo/common/common_type_converters.h +++ b/mojo/common/common_type_converters.h @@ -46,6 +46,11 @@ struct MOJO_COMMON_EXPORT TypeConverter, std::string> { static Array Convert(const std::string& input); }; +template <> +struct MOJO_COMMON_EXPORT TypeConverter, base::StringPiece> { + static Array Convert(const base::StringPiece& input); +}; + template <> struct MOJO_COMMON_EXPORT TypeConverter> { static base::string16 Convert(const Array& input); diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn index 3a2209436ac3b2..8e5e7f23349665 100644 --- a/ui/base/BUILD.gn +++ b/ui/base/BUILD.gn @@ -74,7 +74,6 @@ component("base") { "clipboard/clipboard_util_win.h", "clipboard/clipboard_win.cc", "clipboard/clipboard_win.h", - "clipboard/custom_data_helper_linux.cc", "clipboard/custom_data_helper_mac.mm", "cocoa/animation_utils.h", "cocoa/appkit_utils.h", diff --git a/ui/base/clipboard/clipboard.cc b/ui/base/clipboard/clipboard.cc index d43954bbc31d39..f8125f32cce99e 100644 --- a/ui/base/clipboard/clipboard.cc +++ b/ui/base/clipboard/clipboard.cc @@ -9,6 +9,7 @@ #include #include "base/logging.h" +#include "base/memory/ptr_util.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/geometry/size.h" @@ -32,32 +33,32 @@ void Clipboard::SetAllowedThreads( } // static -Clipboard* Clipboard::GetForCurrentThread() { +void Clipboard::SetClipboardForCurrentThread( + std::unique_ptr platform_clipboard) { base::AutoLock lock(clipboard_map_lock_.Get()); + base::PlatformThreadId id = Clipboard::GetAndValidateThreadID(); - base::PlatformThreadId id = base::PlatformThread::CurrentId(); - - AllowedThreadsVector* allowed_threads = allowed_threads_.Pointer(); - if (!allowed_threads->empty()) { - bool found = false; - for (AllowedThreadsVector::const_iterator it = allowed_threads->begin(); - it != allowed_threads->end(); ++it) { - if (*it == id) { - found = true; - break; - } - } - - DCHECK(found); + ClipboardMap* clipboard_map = clipboard_map_.Pointer(); + ClipboardMap::const_iterator it = clipboard_map->find(id); + if (it != clipboard_map->end()) { + // This shouldn't happen. The clipboard should not already exist. + NOTREACHED(); } + clipboard_map->insert(std::make_pair(id, std::move(platform_clipboard))); +} + +// static +Clipboard* Clipboard::GetForCurrentThread() { + base::AutoLock lock(clipboard_map_lock_.Get()); + base::PlatformThreadId id = GetAndValidateThreadID(); ClipboardMap* clipboard_map = clipboard_map_.Pointer(); ClipboardMap::const_iterator it = clipboard_map->find(id); if (it != clipboard_map->end()) - return it->second; + return it->second.get(); Clipboard* clipboard = Clipboard::Create(); - clipboard_map->insert(std::make_pair(id, clipboard)); + clipboard_map->insert(std::make_pair(id, base::WrapUnique(clipboard))); return clipboard; } @@ -67,10 +68,8 @@ void Clipboard::DestroyClipboardForCurrentThread() { ClipboardMap* clipboard_map = clipboard_map_.Pointer(); base::PlatformThreadId id = base::PlatformThread::CurrentId(); ClipboardMap::iterator it = clipboard_map->find(id); - if (it != clipboard_map->end()) { - delete it->second; + if (it != clipboard_map->end()) clipboard_map->erase(it); - } } void Clipboard::DispatchObject(ObjectType type, const ObjectMapParams& params) { @@ -132,4 +131,25 @@ void Clipboard::DispatchObject(ObjectType type, const ObjectMapParams& params) { } } +base::PlatformThreadId Clipboard::GetAndValidateThreadID() { + base::PlatformThreadId id = base::PlatformThread::CurrentId(); +#ifndef NDEBUG + AllowedThreadsVector* allowed_threads = allowed_threads_.Pointer(); + if (!allowed_threads->empty()) { + bool found = false; + for (AllowedThreadsVector::const_iterator it = allowed_threads->begin(); + it != allowed_threads->end(); ++it) { + if (*it == id) { + found = true; + break; + } + } + + DCHECK(found); + } +#endif + + return id; +} + } // namespace ui diff --git a/ui/base/clipboard/clipboard.h b/ui/base/clipboard/clipboard.h index 40af4df4f2fe2d..7826b2696df33c 100644 --- a/ui/base/clipboard/clipboard.h +++ b/ui/base/clipboard/clipboard.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -60,9 +61,13 @@ class UI_BASE_EXPORT Clipboard : NON_EXPORTED_BASE(public base::ThreadChecker) { static const char kMimeTypeText[]; static const char kMimeTypeURIList[]; static const char kMimeTypeDownloadURL[]; + static const char kMimeTypeMozillaURL[]; static const char kMimeTypeHTML[]; static const char kMimeTypeRTF[]; static const char kMimeTypePNG[]; + static const char kMimeTypeWebCustomData[]; + static const char kMimeTypeWebkitSmartPaste[]; + static const char kMimeTypePepperCustomData[]; // Platform neutral holder for native data representation of a clipboard type. struct UI_BASE_EXPORT FormatType { @@ -137,6 +142,13 @@ class UI_BASE_EXPORT Clipboard : NON_EXPORTED_BASE(public base::ThreadChecker) { static void SetAllowedThreads( const std::vector& allowed_threads); + // Sets the clipboard for the current thread. Previously, there was only + // one clipboard implementation on a platform; now that mus exists, during + // mus app startup, we need to specifically initialize mus instead of the + // current platform clipboard. We take ownership of |platform_clipboard|. + static void SetClipboardForCurrentThread( + std::unique_ptr platform_clipboard); + // Returns the clipboard object for the current thread. // // Most implementations will have at most one clipboard which will live on @@ -308,8 +320,13 @@ class UI_BASE_EXPORT Clipboard : NON_EXPORTED_BASE(public base::ThreadChecker) { private: // For access to WriteObjects(). + friend class ForwardingTestingClipboard; friend class ScopedClipboardWriter; friend class TestClipboard; + // For SetClipboardForCurrentThread's argument. + friend struct std::default_delete; + + static base::PlatformThreadId GetAndValidateThreadID(); // A list of allowed threads. By default, this is empty and no thread checking // is done (in the unit test case), but a user (like content) can set which @@ -318,7 +335,8 @@ class UI_BASE_EXPORT Clipboard : NON_EXPORTED_BASE(public base::ThreadChecker) { static base::LazyInstance allowed_threads_; // Mapping from threads to clipboard objects. - typedef std::map ClipboardMap; + typedef std::map> + ClipboardMap; static base::LazyInstance clipboard_map_; // Mutex that controls access to |g_clipboard_map|. diff --git a/ui/base/clipboard/clipboard_android.cc b/ui/base/clipboard/clipboard_android.cc index 86d86b5be1d4d0..fa62fb638a0585 100644 --- a/ui/base/clipboard/clipboard_android.cc +++ b/ui/base/clipboard/clipboard_android.cc @@ -43,8 +43,6 @@ const char kRTFFormat[] = "rtf"; const char kBitmapFormat[] = "bitmap"; const char kWebKitSmartPasteFormat[] = "webkit_smart"; const char kBookmarkFormat[] = "bookmark"; -const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data"; -const char kMimeTypeWebCustomData[] = "chromium/x-web-custom-data"; class ClipboardMap { public: diff --git a/ui/base/clipboard/clipboard_aura.cc b/ui/base/clipboard/clipboard_aura.cc index e4981a27cdcc2a..fa93a227cf8061 100644 --- a/ui/base/clipboard/clipboard_aura.cc +++ b/ui/base/clipboard/clipboard_aura.cc @@ -24,8 +24,6 @@ namespace ui { namespace { const char kMimeTypeFilename[] = "chromium/filename"; const char kMimeTypeBitmap[] = "image/bmp"; -const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data"; -const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste"; const size_t kMaxClipboardSize = 1; // Clipboard data format used by AuraClipboard. @@ -456,6 +454,12 @@ const Clipboard::FormatType& Clipboard::GetUrlWFormatType() { return GetUrlFormatType(); } +// static +const Clipboard::FormatType& Clipboard::GetMozUrlFormatType() { + CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeMozillaURL)); + return type; +} + // static const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeText)); diff --git a/ui/base/clipboard/clipboard_aurax11.cc b/ui/base/clipboard/clipboard_aurax11.cc index 8c7605e2e0b8d9..f27c4e235add51 100644 --- a/ui/base/clipboard/clipboard_aurax11.cc +++ b/ui/base/clipboard/clipboard_aurax11.cc @@ -41,25 +41,21 @@ namespace { const char kClipboard[] = "CLIPBOARD"; const char kClipboardManager[] = "CLIPBOARD_MANAGER"; const char kMimeTypeFilename[] = "chromium/filename"; -const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data"; -const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste"; const char kSaveTargets[] = "SAVE_TARGETS"; const char kTargets[] = "TARGETS"; -const char* kAtomsToCache[] = { - kClipboard, - kClipboardManager, - Clipboard::kMimeTypePNG, - kMimeTypeFilename, - kMimeTypeMozillaURL, - kMimeTypeWebkitSmartPaste, - kSaveTargets, - kString, - kTargets, - kText, - kUtf8String, - NULL -}; +const char* kAtomsToCache[] = {kClipboard, + kClipboardManager, + Clipboard::kMimeTypePNG, + kMimeTypeFilename, + Clipboard::kMimeTypeMozillaURL, + Clipboard::kMimeTypeWebkitSmartPaste, + kSaveTargets, + kString, + kTargets, + kText, + kUtf8String, + nullptr}; /////////////////////////////////////////////////////////////////////////////// @@ -596,6 +592,12 @@ const Clipboard::FormatType& Clipboard::GetUrlWFormatType() { return GetUrlFormatType(); } +// static +const Clipboard::FormatType& Clipboard::GetMozUrlFormatType() { + CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeMozillaURL)); + return type; +} + // static const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeText)); diff --git a/ui/base/clipboard/clipboard_constants.cc b/ui/base/clipboard/clipboard_constants.cc index 2d499dcf4d0b82..6372353925ea40 100644 --- a/ui/base/clipboard/clipboard_constants.cc +++ b/ui/base/clipboard/clipboard_constants.cc @@ -8,9 +8,15 @@ namespace ui { const char Clipboard::kMimeTypeText[] = "text/plain"; const char Clipboard::kMimeTypeURIList[] = "text/uri-list"; +const char Clipboard::kMimeTypeMozillaURL[] = "text/x-moz-url"; const char Clipboard::kMimeTypeDownloadURL[] = "downloadurl"; const char Clipboard::kMimeTypeHTML[] = "text/html"; const char Clipboard::kMimeTypeRTF[] = "text/rtf"; const char Clipboard::kMimeTypePNG[] = "image/png"; +// TODO(dcheng): This name is temporary. See crbug.com/106449. +const char Clipboard::kMimeTypeWebCustomData[] = "chromium/x-web-custom-data"; +const char Clipboard::kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste"; +const char Clipboard::kMimeTypePepperCustomData[] = + "chromium/x-pepper-custom-data"; } // namespace ui diff --git a/ui/base/clipboard/clipboard_test_template.h b/ui/base/clipboard/clipboard_test_template.h index b2deb2210824c9..cf033779cefc50 100644 --- a/ui/base/clipboard/clipboard_test_template.h +++ b/ui/base/clipboard/clipboard_test_template.h @@ -56,7 +56,7 @@ class ClipboardTest : public PlatformTest { public: #if defined(USE_AURA) ClipboardTest() - : event_source_(PlatformEventSource::CreateDefault()), + : event_source_(ClipboardTraits::GetEventSource()), clipboard_(ClipboardTraits::Create()) {} #else ClipboardTest() : clipboard_(ClipboardTraits::Create()) {} @@ -64,6 +64,8 @@ class ClipboardTest : public PlatformTest { ~ClipboardTest() override { ClipboardTraits::Destroy(clipboard_); } + bool IsMusTest() { return ClipboardTraits::IsMusTest(); } + protected: Clipboard& clipboard() { return *clipboard_; } @@ -86,6 +88,7 @@ class ClipboardTest : public PlatformTest { // Hack for tests that need to call static methods of ClipboardTest. struct NullClipboardTraits { static Clipboard* Create() { return nullptr; } + static bool IsMusTest() { return false; } static void Destroy(Clipboard*) {} }; @@ -366,9 +369,11 @@ TYPED_TEST(ClipboardTest, URLTest) { #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && \ !defined(OS_CHROMEOS) - ascii_text.clear(); - this->clipboard().ReadAsciiText(CLIPBOARD_TYPE_SELECTION, &ascii_text); - EXPECT_EQ(UTF16ToUTF8(url), ascii_text); + if (!this->IsMusTest()) { + ascii_text.clear(); + this->clipboard().ReadAsciiText(CLIPBOARD_TYPE_SELECTION, &ascii_text); + EXPECT_EQ(UTF16ToUTF8(url), ascii_text); + } #endif } @@ -444,7 +449,7 @@ TYPED_TEST(ClipboardTest, DataTest) { this->clipboard().ReadData(kFormat, &output); ASSERT_FALSE(output.empty()); - base::Pickle read_pickle(output.data(), output.size()); + base::Pickle read_pickle(output.data(), static_cast(output.size())); base::PickleIterator iter(read_pickle); std::string unpickled_string; ASSERT_TRUE(iter.ReadString(&unpickled_string)); @@ -479,7 +484,7 @@ TYPED_TEST(ClipboardTest, MultipleDataTest) { this->clipboard().ReadData(kFormat2, &output2); ASSERT_FALSE(output2.empty()); - base::Pickle read_pickle2(output2.data(), output2.size()); + base::Pickle read_pickle2(output2.data(), static_cast(output2.size())); base::PickleIterator iter2(read_pickle2); std::string unpickled_string2; ASSERT_TRUE(iter2.ReadString(&unpickled_string2)); @@ -500,7 +505,7 @@ TYPED_TEST(ClipboardTest, MultipleDataTest) { this->clipboard().ReadData(kFormat1, &output1); ASSERT_FALSE(output1.empty()); - base::Pickle read_pickle1(output1.data(), output1.size()); + base::Pickle read_pickle1(output1.data(), static_cast(output1.size())); base::PickleIterator iter1(read_pickle1); std::string unpickled_string1; ASSERT_TRUE(iter1.ReadString(&unpickled_string1)); diff --git a/ui/base/clipboard/clipboard_unittest.cc b/ui/base/clipboard/clipboard_unittest.cc index 96bf40b4ec4dd6..a776674e29d4de 100644 --- a/ui/base/clipboard/clipboard_unittest.cc +++ b/ui/base/clipboard/clipboard_unittest.cc @@ -6,11 +6,23 @@ #include "testing/gtest/include/gtest/gtest.h" +#if defined(USE_AURA) +#include "ui/events/platform/platform_event_source.h" +#endif + namespace ui { struct PlatformClipboardTraits { +#if defined(USE_AURA) + static std::unique_ptr GetEventSource() { + return PlatformEventSource::CreateDefault(); + } +#endif + static Clipboard* Create() { return Clipboard::GetForCurrentThread(); } + static bool IsMusTest() { return false; } + static void Destroy(Clipboard* clipboard) { ASSERT_EQ(Clipboard::GetForCurrentThread(), clipboard); Clipboard::DestroyClipboardForCurrentThread(); diff --git a/ui/base/clipboard/custom_data_helper.h b/ui/base/clipboard/custom_data_helper.h index 55a2b3bb22c546..c4eebc78eb8ca8 100644 --- a/ui/base/clipboard/custom_data_helper.h +++ b/ui/base/clipboard/custom_data_helper.h @@ -35,8 +35,6 @@ namespace ui { #if defined(OS_MACOSX) && !defined(USE_AURA) UI_BASE_EXPORT extern NSString* const kWebCustomDataPboardType; -#elif !defined(OS_WIN) && defined(USE_AURA) -UI_BASE_EXPORT extern const char kMimeTypeWebCustomData[]; #endif UI_BASE_EXPORT void ReadCustomDataTypes(const void* data, diff --git a/ui/base/clipboard/custom_data_helper_linux.cc b/ui/base/clipboard/custom_data_helper_linux.cc deleted file mode 100644 index d8cc89edff558d..00000000000000 --- a/ui/base/clipboard/custom_data_helper_linux.cc +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "build/build_config.h" -#include "ui/base/clipboard/custom_data_helper.h" - -namespace ui { - -// TODO(dcheng): This name is temporary. See crbug.com/106449 -const char kMimeTypeWebCustomData[] = "chromium/x-web-custom-data"; - -} // namespace ui diff --git a/ui/base/clipboard/custom_data_helper_mac.mm b/ui/base/clipboard/custom_data_helper_mac.mm index ae4362194f8e19..587d7b41a9e29d 100644 --- a/ui/base/clipboard/custom_data_helper_mac.mm +++ b/ui/base/clipboard/custom_data_helper_mac.mm @@ -9,9 +9,7 @@ namespace ui { // TODO(dcheng): This name is temporary. See crbug.com/106449. -#if defined(USE_AURA) -const char kMimeTypeWebCustomData[] = "chromium/x-web-custom-data"; -#else +#if !defined(USE_AURA) NSString* const kWebCustomDataPboardType = @"org.chromium.web-custom-data"; #endif diff --git a/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc b/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc index e2bba47010817f..60486709d1c9b4 100644 --- a/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc +++ b/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc @@ -31,18 +31,16 @@ const char kRendererTaint[] = "chromium/x-renderer-taint"; const char kNetscapeURL[] = "_NETSCAPE_URL"; -const char* kAtomsToCache[] = { - kString, - kText, - kUtf8String, - kDndSelection, - Clipboard::kMimeTypeURIList, - kMimeTypeMozillaURL, - kNetscapeURL, - Clipboard::kMimeTypeText, - kRendererTaint, - NULL -}; +const char* kAtomsToCache[] = {kString, + kText, + kUtf8String, + kDndSelection, + Clipboard::kMimeTypeURIList, + Clipboard::kMimeTypeMozillaURL, + kNetscapeURL, + Clipboard::kMimeTypeText, + kRendererTaint, + nullptr}; } // namespace @@ -156,7 +154,8 @@ void OSExchangeDataProviderAuraX11::SetURL(const GURL& url, scoped_refptr mem( base::RefCountedBytes::TakeVector(&data)); - format_map_.Insert(atom_cache_.GetAtom(kMimeTypeMozillaURL), mem); + format_map_.Insert(atom_cache_.GetAtom(Clipboard::kMimeTypeMozillaURL), + mem); // Set a string fallback as well. SetString(spec); @@ -255,7 +254,7 @@ bool OSExchangeDataProviderAuraX11::GetURLAndTitle( // but that doesn't match the assumptions of the rest of the system which // expect single types. - if (data.GetType() == atom_cache_.GetAtom(kMimeTypeMozillaURL)) { + if (data.GetType() == atom_cache_.GetAtom(Clipboard::kMimeTypeMozillaURL)) { // Mozilla URLs are (UTF16: URL, newline, title). base::string16 unparsed; data.AssignTo(&unparsed); @@ -362,7 +361,7 @@ bool OSExchangeDataProviderAuraX11::HasURL( // Windows does and stuffs all the data into one mime type. ui::SelectionData data(format_map_.GetFirstOf(requested_types)); if (data.IsValid()) { - if (data.GetType() == atom_cache_.GetAtom(kMimeTypeMozillaURL)) { + if (data.GetType() == atom_cache_.GetAtom(Clipboard::kMimeTypeMozillaURL)) { // File managers shouldn't be using this type, so this is a URL. return true; } else if (data.GetType() == atom_cache_.GetAtom( @@ -423,7 +422,7 @@ void OSExchangeDataProviderAuraX11::SetFileContents( const std::string& file_contents) { DCHECK(!filename.empty()); DCHECK(format_map_.end() == - format_map_.find(atom_cache_.GetAtom(kMimeTypeMozillaURL))); + format_map_.find(atom_cache_.GetAtom(Clipboard::kMimeTypeMozillaURL))); file_contents_name_ = filename; diff --git a/ui/base/test/test_clipboard.cc b/ui/base/test/test_clipboard.cc index 2971149683e026..0eb8cb98a0c1bc 100644 --- a/ui/base/test/test_clipboard.cc +++ b/ui/base/test/test_clipboard.cc @@ -5,6 +5,7 @@ #include "ui/base/test/test_clipboard.h" #include +#include "base/memory/ptr_util.h" #include "base/numerics/safe_conversions.h" #include "base/strings/utf_string_conversions.h" @@ -21,7 +22,7 @@ Clipboard* TestClipboard::CreateForCurrentThread() { base::AutoLock lock(Clipboard::clipboard_map_lock_.Get()); Clipboard* clipboard = new TestClipboard; Clipboard::clipboard_map_.Get()[base::PlatformThread::CurrentId()] = - clipboard; + base::WrapUnique(clipboard); return clipboard; } diff --git a/ui/base/test/test_clipboard_unittest.cc b/ui/base/test/test_clipboard_unittest.cc index 8d3a4367428634..da1b8fc357e5b3 100644 --- a/ui/base/test/test_clipboard_unittest.cc +++ b/ui/base/test/test_clipboard_unittest.cc @@ -6,11 +6,23 @@ #include "testing/gtest/include/gtest/gtest.h" +#if defined(USE_AURA) +#include "ui/events/platform/platform_event_source.h" +#endif + namespace ui { struct TestClipboardTraits { +#if defined(USE_AURA) + static std::unique_ptr GetEventSource() { + return nullptr; + } +#endif + static Clipboard* Create() { return TestClipboard::CreateForCurrentThread(); } + static bool IsMusTest() { return false; } + static void Destroy(Clipboard* clipboard) { ASSERT_EQ(Clipboard::GetForCurrentThread(), clipboard); Clipboard::DestroyClipboardForCurrentThread(); diff --git a/ui/base/ui_base.gyp b/ui/base/ui_base.gyp index f7e4145f7a744f..0d5cd1b016dff3 100644 --- a/ui/base/ui_base.gyp +++ b/ui/base/ui_base.gyp @@ -123,7 +123,6 @@ 'clipboard/clipboard_win.h', 'clipboard/custom_data_helper.cc', 'clipboard/custom_data_helper.h', - 'clipboard/custom_data_helper_linux.cc', 'clipboard/custom_data_helper_mac.mm', 'clipboard/scoped_clipboard_writer.cc', 'clipboard/scoped_clipboard_writer.h', diff --git a/ui/base/x/selection_utils.cc b/ui/base/x/selection_utils.cc index d7acd52c029070..2d57e496563e48 100644 --- a/ui/base/x/selection_utils.cc +++ b/ui/base/x/selection_utils.cc @@ -20,7 +20,6 @@ namespace ui { -const char kMimeTypeMozillaURL[] = "text/x-moz-url"; const char kString[] = "STRING"; const char kText[] = "TEXT"; const char kTextPlain[] = "text/plain"; @@ -50,7 +49,7 @@ std::vector< ::Atom> GetTextAtomsFrom(const X11AtomCache* atom_cache) { std::vector< ::Atom> GetURLAtomsFrom(const X11AtomCache* atom_cache) { std::vector< ::Atom> atoms; atoms.push_back(atom_cache->GetAtom(Clipboard::kMimeTypeURIList)); - atoms.push_back(atom_cache->GetAtom(kMimeTypeMozillaURL)); + atoms.push_back(atom_cache->GetAtom(Clipboard::kMimeTypeMozillaURL)); return atoms; } diff --git a/ui/base/x/selection_utils.h b/ui/base/x/selection_utils.h index addbcf7db21140..1f911a3ce97127 100644 --- a/ui/base/x/selection_utils.h +++ b/ui/base/x/selection_utils.h @@ -22,7 +22,6 @@ namespace ui { class SelectionData; class X11AtomCache; -extern const char kMimeTypeMozillaURL[]; extern const char kString[]; extern const char kText[]; extern const char kUtf8String[]; diff --git a/ui/views/mus/BUILD.gn b/ui/views/mus/BUILD.gn index 065d32ca15106d..e770bf37cee2f7 100644 --- a/ui/views/mus/BUILD.gn +++ b/ui/views/mus/BUILD.gn @@ -15,6 +15,8 @@ component("mus") { sources = [ "aura_init.cc", "aura_init.h", + "clipboard_mus.cc", + "clipboard_mus.h", "display_list.cc", "display_list.h", "input_method_mus.cc", @@ -58,6 +60,7 @@ component("mus") { "//components/mus/public/cpp", "//components/mus/public/cpp/surfaces", "//components/mus/public/interfaces", + "//mojo/common", "//mojo/public/c/system:for_component", "//mojo/public/cpp/bindings", "//services/catalog/public/cpp", @@ -249,6 +252,7 @@ test("views_mus_interactive_ui_tests") { sources = [ "../widget/widget_interactive_uitest.cc", + "clipboard_unittest.cc", "interactive_ui_tests_mus.cc", ] @@ -257,6 +261,8 @@ test("views_mus_interactive_ui_tests") { ":test_support", "//base", "//services/shell/background:main", # Provides main(). + "//testing/gmock", + "//testing/gtest", "//ui/aura", "//ui/base", "//ui/base/ime", diff --git a/ui/views/mus/DEPS b/ui/views/mus/DEPS index 9f23b5b7592291..84766d2fe9afce 100644 --- a/ui/views/mus/DEPS +++ b/ui/views/mus/DEPS @@ -6,6 +6,7 @@ include_rules = [ "+components/gpu", "+components/mus", "+mojo/cc", + "+mojo/common", "+mojo/converters", "+mojo/public", "+services/catalog/public", diff --git a/ui/views/mus/clipboard_mus.cc b/ui/views/mus/clipboard_mus.cc new file mode 100644 index 00000000000000..2946627b35272d --- /dev/null +++ b/ui/views/mus/clipboard_mus.cc @@ -0,0 +1,338 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/mus/clipboard_mus.h" + +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/thread_restrictions.h" +#include "mojo/common/common_type_converters.h" +#include "services/shell/public/cpp/connector.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/base/clipboard/custom_data_helper.h" +#include "ui/gfx/codec/png_codec.h" + +namespace views { +namespace { + +mus::mojom::Clipboard::Type GetType(ui::ClipboardType type) { + switch (type) { + case ui::CLIPBOARD_TYPE_COPY_PASTE: + return mus::mojom::Clipboard::Type::COPY_PASTE; + case ui::CLIPBOARD_TYPE_SELECTION: + return mus::mojom::Clipboard::Type::SELECTION; + case ui::CLIPBOARD_TYPE_DRAG: + return mus::mojom::Clipboard::Type::DRAG; + } + + NOTREACHED(); + return mus::mojom::Clipboard::Type::COPY_PASTE; +} + +// The source URL of copied HTML. +const char kInternalSourceURL[] = "chromium/internal-url"; + +} // namespace + +ClipboardMus::ClipboardMus() {} + +ClipboardMus::~ClipboardMus() {} + +void ClipboardMus::Init(shell::Connector* connector) { + connector->ConnectToInterface("mojo:mus", &clipboard_); +} + +// TODO(erg): This isn't optimal. It would be better to move the entire +// FormatType system to mime types throughout chrome, but that's a very large +// change. +mojo::String ClipboardMus::GetMimeTypeFor(const FormatType& format) { + if (format.Equals(GetUrlFormatType()) || format.Equals(GetUrlWFormatType())) + return mus::mojom::kMimeTypeURIList; + if (format.Equals(GetMozUrlFormatType())) + return mus::mojom::kMimeTypeMozillaURL; + if (format.Equals(GetPlainTextFormatType()) || + format.Equals(GetPlainTextWFormatType())) { + return mus::mojom::kMimeTypeText; + } + if (format.Equals(GetHtmlFormatType())) + return mus::mojom::kMimeTypeHTML; + if (format.Equals(GetRtfFormatType())) + return mus::mojom::kMimeTypeRTF; + if (format.Equals(GetBitmapFormatType())) + return mus::mojom::kMimeTypePNG; + if (format.Equals(GetWebKitSmartPasteFormatType())) + return kMimeTypeWebkitSmartPaste; + if (format.Equals(GetWebCustomDataFormatType())) + return kMimeTypeWebCustomData; + if (format.Equals(GetPepperCustomDataFormatType())) + return kMimeTypePepperCustomData; + + // TODO(erg): This isn't optimal, but it's the best we can do. On windows, + // this will return strings that aren't MIME types, though they'll be + // unique and should be serializable on the other side of the mojo + // connection. + return format.Serialize(); +} + +bool ClipboardMus::HasMimeType(const mojo::Array& available_types, + const std::string& type) const { + return ContainsValue(available_types, type); +} + +uint64_t ClipboardMus::GetSequenceNumber(ui::ClipboardType type) const { + base::ThreadRestrictions::ScopedAllowWait allow_wait; + uint64_t sequence_number = 0; + clipboard_->GetSequenceNumber(GetType(type), &sequence_number); + return sequence_number; +} + +bool ClipboardMus::IsFormatAvailable(const FormatType& format, + ui::ClipboardType type) const { + base::ThreadRestrictions::ScopedAllowWait allow_wait; + + uint64_t sequence_number = 0; + mojo::Array available_types; + clipboard_->GetAvailableMimeTypes(GetType(type), &sequence_number, + &available_types); + + mojo::String format_in_mime = GetMimeTypeFor(format); + return ContainsValue(available_types, format_in_mime); +} + +void ClipboardMus::Clear(ui::ClipboardType type) { + // Sends the data to mus server. + uint64_t sequence_number = 0; + base::ThreadRestrictions::ScopedAllowWait allow_wait; + clipboard_->WriteClipboardData(GetType(type), nullptr, + &sequence_number); +} + +void ClipboardMus::ReadAvailableTypes(ui::ClipboardType type, + std::vector* types, + bool* contains_filenames) const { + base::ThreadRestrictions::ScopedAllowWait allow_wait; + + uint64_t sequence_number = 0; + mojo::Array available_types; + clipboard_->GetAvailableMimeTypes(GetType(type), &sequence_number, + &available_types); + + types->clear(); + if (HasMimeType(available_types, mus::mojom::kMimeTypeText)) + types->push_back(base::UTF8ToUTF16(mus::mojom::kMimeTypeText)); + if (HasMimeType(available_types, mus::mojom::kMimeTypeHTML)) + types->push_back(base::UTF8ToUTF16(mus::mojom::kMimeTypeHTML)); + if (HasMimeType(available_types, mus::mojom::kMimeTypeRTF)) + types->push_back(base::UTF8ToUTF16(mus::mojom::kMimeTypeRTF)); + if (HasMimeType(available_types, mus::mojom::kMimeTypePNG)) + types->push_back(base::UTF8ToUTF16(mus::mojom::kMimeTypePNG)); + + if (HasMimeType(available_types, kMimeTypeWebCustomData)) { + mojo::Array custom_data; + uint64_t sequence_number = 0; + if (clipboard_->ReadClipboardData(GetType(type), kMimeTypeWebCustomData, + &sequence_number, &custom_data)) { + ui::ReadCustomDataTypes(&custom_data.front(), custom_data.size(), types); + } + } + + *contains_filenames = false; +} + +void ClipboardMus::ReadText(ui::ClipboardType type, + base::string16* result) const { + base::ThreadRestrictions::ScopedAllowWait allow_wait; + mojo::Array text_data; + uint64_t sequence_number = 0; + if (clipboard_->ReadClipboardData(GetType(type), + mojo::String(mus::mojom::kMimeTypeText), + &sequence_number, &text_data)) { + std::string text = text_data.To(); + *result = base::UTF8ToUTF16(text); + } +} + +void ClipboardMus::ReadAsciiText(ui::ClipboardType type, + std::string* result) const { + base::ThreadRestrictions::ScopedAllowWait allow_wait; + mojo::Array text_data; + uint64_t sequence_number = 0; + if (clipboard_->ReadClipboardData(GetType(type), + mojo::String(mus::mojom::kMimeTypeText), + &sequence_number, &text_data)) { + *result = text_data.To(); + } +} + +void ClipboardMus::ReadHTML(ui::ClipboardType type, + base::string16* markup, + std::string* src_url, + uint32_t* fragment_start, + uint32_t* fragment_end) const { + markup->clear(); + if (src_url) + src_url->clear(); + *fragment_start = 0; + *fragment_end = 0; + + base::ThreadRestrictions::ScopedAllowWait allow_wait; + mojo::Array html_data; + uint64_t sequence_number = 0; + if (clipboard_->ReadClipboardData(GetType(type), + mojo::String(mus::mojom::kMimeTypeHTML), + &sequence_number, &html_data)) { + *markup = base::UTF8ToUTF16(html_data.To()); + *fragment_end = static_cast(markup->length()); + + // We only bother fetching the source url if we were the ones who wrote + // this html data to the clipboard. + mojo::Array url_data; + if (clipboard_->ReadClipboardData(GetType(type), kInternalSourceURL, + &sequence_number, &url_data)) { + *src_url = url_data.To(); + } + } +} + +void ClipboardMus::ReadRTF(ui::ClipboardType type, std::string* result) const { + base::ThreadRestrictions::ScopedAllowWait allow_wait; + mojo::Array rtf_data; + uint64_t sequence_number = 0; + if (clipboard_->ReadClipboardData( + GetType(type), mojo::String(mus::mojom::kMimeTypeRTF), + &sequence_number, &rtf_data)) { + *result = rtf_data.To(); + } +} + +SkBitmap ClipboardMus::ReadImage(ui::ClipboardType type) const { + base::ThreadRestrictions::ScopedAllowWait allow_wait; + mojo::Array data; + uint64_t sequence_number = 0; + if (clipboard_->ReadClipboardData( + GetType(type), mojo::String(mus::mojom::kMimeTypePNG), + &sequence_number, &data)) { + SkBitmap bitmap; + if (gfx::PNGCodec::Decode(&data.front(), data.size(), &bitmap)) + return SkBitmap(bitmap); + } + + return SkBitmap(); +} + +void ClipboardMus::ReadCustomData(ui::ClipboardType clipboard_type, + const base::string16& type, + base::string16* result) const { + base::ThreadRestrictions::ScopedAllowWait allow_wait; + mojo::Array custom_data; + uint64_t sequence_number = 0; + if (clipboard_->ReadClipboardData(GetType(clipboard_type), + mojo::String(kMimeTypeWebCustomData), + &sequence_number, &custom_data)) { + ui::ReadCustomDataForType(&custom_data.front(), custom_data.size(), type, + result); + } +} + +void ClipboardMus::ReadBookmark(base::string16* title, std::string* url) const { + // TODO(erg): This is NOTIMPLEMENTED() on all linux platforms? + NOTIMPLEMENTED(); +} + +void ClipboardMus::ReadData(const FormatType& format, + std::string* result) const { + base::ThreadRestrictions::ScopedAllowWait allow_wait; + mojo::Array data; + uint64_t sequence_number = 0; + if (clipboard_->ReadClipboardData(mus::mojom::Clipboard::Type::COPY_PASTE, + GetMimeTypeFor(format), + &sequence_number, &data)) { + *result = data.To(); + } +} + +void ClipboardMus::WriteObjects(ui::ClipboardType type, + const ObjectMap& objects) { + current_clipboard_.reset(new mojo::Map>); + for (const auto& p : objects) + DispatchObject(static_cast(p.first), p.second); + + // Sends the data to mus server. + uint64_t sequence_number = 0; + base::ThreadRestrictions::ScopedAllowWait allow_wait; + clipboard_->WriteClipboardData(GetType(type), std::move(*current_clipboard_), + &sequence_number); + current_clipboard_.reset(); +} + +void ClipboardMus::WriteText(const char* text_data, size_t text_len) { + DCHECK(current_clipboard_); + current_clipboard_->insert( + mus::mojom::kMimeTypeText, + mojo::Array::From(base::StringPiece(text_data, text_len))); +} + +void ClipboardMus::WriteHTML(const char* markup_data, + size_t markup_len, + const char* url_data, + size_t url_len) { + DCHECK(current_clipboard_); + current_clipboard_->insert( + mus::mojom::kMimeTypeHTML, + mojo::Array::From(base::StringPiece(markup_data, markup_len))); + if (url_len > 0) { + current_clipboard_->insert( + kInternalSourceURL, + mojo::Array::From(base::StringPiece(url_data, url_len))); + } +} + +void ClipboardMus::WriteRTF(const char* rtf_data, size_t data_len) { + DCHECK(current_clipboard_); + current_clipboard_->insert( + mus::mojom::kMimeTypeRTF, + mojo::Array::From(base::StringPiece(rtf_data, data_len))); +} + +void ClipboardMus::WriteBookmark(const char* title_data, + size_t title_len, + const char* url_data, + size_t url_len) { + // Writes a Mozilla url (UTF16: URL, newline, title) + base::string16 bookmark = + base::UTF8ToUTF16(base::StringPiece(url_data, url_len)) + + base::ASCIIToUTF16("\n") + + base::UTF8ToUTF16(base::StringPiece(title_data, title_len)); + + DCHECK(current_clipboard_); + current_clipboard_->insert(mus::mojom::kMimeTypeMozillaURL, + mojo::Array::From(bookmark)); +} + +void ClipboardMus::WriteWebSmartPaste() { + DCHECK(current_clipboard_); + current_clipboard_->insert(kMimeTypeWebkitSmartPaste, mojo::Array()); +} + +void ClipboardMus::WriteBitmap(const SkBitmap& bitmap) { + DCHECK(current_clipboard_); + // Encode the bitmap as a PNG for transport. + std::vector output; + if (gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, false, &output)) { + current_clipboard_->insert(mus::mojom::kMimeTypePNG, + mojo::Array::From(output)); + } +} + +void ClipboardMus::WriteData(const FormatType& format, + const char* data_data, + size_t data_len) { + DCHECK(current_clipboard_); + current_clipboard_->insert( + GetMimeTypeFor(format), + mojo::Array::From(base::StringPiece(data_data, data_len))); +} + +} // namespace views diff --git a/ui/views/mus/clipboard_mus.h b/ui/views/mus/clipboard_mus.h new file mode 100644 index 00000000000000..ddfa9fa6d1b671 --- /dev/null +++ b/ui/views/mus/clipboard_mus.h @@ -0,0 +1,87 @@ +// Copyright 2016 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_VIEWS_MUS_CLIPBOARD_MUS_H_ +#define UI_VIEWS_MUS_CLIPBOARD_MUS_H_ + +#include "components/mus/public/interfaces/clipboard.mojom.h" +#include "ui/base/clipboard/clipboard.h" +#include "ui/views/mus/mus_export.h" + +namespace shell { +class Connector; +} + +namespace views { + +// An adaptor class which translates the ui::Clipboard interface to the +// clipboard provided by mus. +class VIEWS_MUS_EXPORT ClipboardMus : public ui::Clipboard { + public: + ClipboardMus(); + ~ClipboardMus() override; + + void Init(shell::Connector* connector); + + private: + bool HasMimeType(const mojo::Array& available_types, + const std::string& type) const; + + // Clipboard overrides: + uint64_t GetSequenceNumber(ui::ClipboardType type) const override; + bool IsFormatAvailable(const FormatType& format, + ui::ClipboardType type) const override; + void Clear(ui::ClipboardType type) override; + void ReadAvailableTypes(ui::ClipboardType type, + std::vector* types, + bool* contains_filenames) const override; + void ReadText(ui::ClipboardType type, base::string16* result) const override; + void ReadAsciiText(ui::ClipboardType type, + std::string* result) const override; + void ReadHTML(ui::ClipboardType type, + base::string16* markup, + std::string* src_url, + uint32_t* fragment_start, + uint32_t* fragment_end) const override; + void ReadRTF(ui::ClipboardType type, std::string* result) const override; + SkBitmap ReadImage(ui::ClipboardType type) const override; + void ReadCustomData(ui::ClipboardType clipboard_type, + const base::string16& type, + base::string16* result) const override; + void ReadBookmark(base::string16* title, std::string* url) const override; + void ReadData(const FormatType& format, std::string* result) const override; + void WriteObjects(ui::ClipboardType type, const ObjectMap& objects) override; + void WriteText(const char* text_data, size_t text_len) override; + void WriteHTML(const char* markup_data, + size_t markup_len, + const char* url_data, + size_t url_len) override; + void WriteRTF(const char* rtf_data, size_t data_len) override; + void WriteBookmark(const char* title_data, + size_t title_len, + const char* url_data, + size_t url_len) override; + void WriteWebSmartPaste() override; + void WriteBitmap(const SkBitmap& bitmap) override; + void WriteData(const FormatType& format, + const char* data_data, + size_t data_len) override; + + static mojo::String GetMimeTypeFor(const FormatType& format); + + mus::mojom::ClipboardPtr clipboard_; + + // Internal buffer used to accumulate data types. The public interface is + // WriteObjects(), which then calls our base class DispatchObject() which + // then calls into each data type specific Write() function. Once we've + // collected all the data types, we then pass this to the mus server. + std::unique_ptr>> + current_clipboard_; + + DISALLOW_COPY_AND_ASSIGN(ClipboardMus); +}; + +} // namespace views + +#endif // UI_VIEWS_MUS_CLIPBOARD_MUS_H_ diff --git a/ui/views/mus/clipboard_unittest.cc b/ui/views/mus/clipboard_unittest.cc new file mode 100644 index 00000000000000..b2825977525fb4 --- /dev/null +++ b/ui/views/mus/clipboard_unittest.cc @@ -0,0 +1,145 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/mus/clipboard_mus.h" + +#include "base/message_loop/message_loop.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/views/mus/window_manager_connection.h" +#include "ui/views/test/scoped_views_test_helper.h" + +namespace ui { + +// So we can't make ScopedViewsTestHelper a global. We must set up +// ScopedViewsTestHelper on every test (which will create the connection to +// mus). And we can't modify PlatformClipboardTraits to not be a pure static +// struct. So to solve these lifetime issues, create an adapter that owns the +// ScopedViewsTestHelper and then . +class ForwardingTestingClipboard : public ui::Clipboard { + public: + ForwardingTestingClipboard() + : test_helper_(new views::ScopedViewsTestHelper), + clipboard_to_test_(Clipboard::GetForCurrentThread()) { + // If we don't have a window manager connection, we will get the default + // platform clipboard instead. + EXPECT_TRUE(views::WindowManagerConnection::Exists()); + } + + ~ForwardingTestingClipboard() override { + Clipboard::DestroyClipboardForCurrentThread(); + } + + void Destroy() { + delete this; + } + + protected: + // Overridden from ui::Clipboard: + uint64_t GetSequenceNumber(ClipboardType type) const override { + return clipboard_to_test_->GetSequenceNumber(type); + } + bool IsFormatAvailable(const FormatType& format, + ClipboardType type) const override { + return clipboard_to_test_->IsFormatAvailable(format, type); + } + void Clear(ClipboardType type) override { + clipboard_to_test_->Clear(type); + } + void ReadAvailableTypes(ClipboardType type, + std::vector* types, + bool* contains_filenames) const override { + clipboard_to_test_->ReadAvailableTypes(type, types, contains_filenames); + } + void ReadText(ClipboardType type, base::string16* result) const override { + clipboard_to_test_->ReadText(type, result); + } + void ReadAsciiText(ClipboardType type, std::string* result) const override { + clipboard_to_test_->ReadAsciiText(type, result); + } + void ReadHTML(ClipboardType type, base::string16* markup, + std::string* src_url, uint32_t* fragment_start, + uint32_t* fragment_end) const override { + clipboard_to_test_->ReadHTML(type, markup, src_url, + fragment_start, fragment_end); + } + void ReadRTF(ClipboardType type, std::string* result) const override { + clipboard_to_test_->ReadRTF(type, result); + } + SkBitmap ReadImage(ClipboardType type) const override { + return clipboard_to_test_->ReadImage(type); + } + void ReadCustomData(ClipboardType clipboard_type, + const base::string16& type, + base::string16* result) const override { + clipboard_to_test_->ReadCustomData(clipboard_type, type, result); + } + void ReadBookmark(base::string16* title, std::string* url) const override { + clipboard_to_test_->ReadBookmark(title, url); + } + void ReadData(const FormatType& format, std::string* result) const override { + clipboard_to_test_->ReadData(format, result); + } + void WriteObjects(ClipboardType type, const ObjectMap& objects) override { + clipboard_to_test_->WriteObjects(type, objects); + } + void WriteText(const char* text_data, size_t text_len) override { + clipboard_to_test_->WriteText(text_data, text_len); + } + void WriteHTML(const char* markup_data, + size_t markup_len, + const char* url_data, + size_t url_len) override { + clipboard_to_test_->WriteHTML(markup_data, markup_len, url_data, url_len); + } + void WriteRTF(const char* rtf_data, size_t data_len) override { + clipboard_to_test_->WriteRTF(rtf_data, data_len); + } + void WriteBookmark(const char* title_data, + size_t title_len, + const char* url_data, + size_t url_len) override { + clipboard_to_test_->WriteBookmark(title_data, title_len, + url_data, url_len); + } + void WriteWebSmartPaste() override { + clipboard_to_test_->WriteWebSmartPaste(); + } + void WriteBitmap(const SkBitmap& bitmap) override { + clipboard_to_test_->WriteBitmap(bitmap); + } + void WriteData(const FormatType& format, + const char* data_data, + size_t data_len) override { + clipboard_to_test_->WriteData(format, data_data, data_len); + } + + private: + std::unique_ptr test_helper_; + ui::Clipboard* clipboard_to_test_; + + DISALLOW_COPY_AND_ASSIGN(ForwardingTestingClipboard); +}; + +struct PlatformClipboardTraits { + static std::unique_ptr GetEventSource() { + return nullptr; + } + + static Clipboard* Create() { + return new ForwardingTestingClipboard(); + } + + static bool IsMusTest() { return true; } + + static void Destroy(Clipboard* clipboard) { + static_cast(clipboard)->Destroy(); + } +}; + +using TypesToTest = PlatformClipboardTraits; + +} // namespace ui + +#include "ui/base/clipboard/clipboard_test_template.h" diff --git a/ui/views/mus/window_manager_connection.cc b/ui/views/mus/window_manager_connection.cc index 5f07c6c645aec9..89c55177cd6460 100644 --- a/ui/views/mus/window_manager_connection.cc +++ b/ui/views/mus/window_manager_connection.cc @@ -17,6 +17,7 @@ #include "services/shell/public/cpp/connection.h" #include "services/shell/public/cpp/connector.h" #include "ui/events/devices/device_data_manager.h" +#include "ui/views/mus/clipboard_mus.h" #include "ui/views/mus/native_widget_mus.h" #include "ui/views/mus/screen_mus.h" #include "ui/views/pointer_watcher.h" @@ -113,6 +114,10 @@ WindowManagerConnection::WindowManagerConnection( screen_.reset(new ScreenMus(this)); screen_->Init(connector); + std::unique_ptr clipboard(new ClipboardMus); + clipboard->Init(connector); + ui::Clipboard::SetClipboardForCurrentThread(std::move(clipboard)); + if (!ui::DeviceDataManager::HasInstance()) { // TODO(sad): We should have a DeviceDataManager implementation that talks // to a mojo service to learn about the input-devices on the system. @@ -131,6 +136,7 @@ WindowManagerConnection::~WindowManagerConnection() { // ~WindowTreeClient calls back to us (we're its delegate), destroy it while // we are still valid. client_.reset(); + ui::Clipboard::DestroyClipboardForCurrentThread(); if (created_device_data_manager_) ui::DeviceDataManager::DeleteInstance(); lazy_tls_ptr.Pointer()->Set(nullptr); diff --git a/ui/views/mus/window_manager_connection.h b/ui/views/mus/window_manager_connection.h index 7bf3bfa7bd78b0..622016401ccd2d 100644 --- a/ui/views/mus/window_manager_connection.h +++ b/ui/views/mus/window_manager_connection.h @@ -22,6 +22,7 @@ class Connector; } namespace views { +class ClipboardMus; class NativeWidget; class PointerWatcher; class ScreenMus; @@ -31,6 +32,7 @@ class NativeWidgetDelegate; // Provides configuration to mus in views. This consists of the following: // . Provides a Screen implementation backed by mus. +// . Provides a Clipboard implementation backed by mus. // . Creates and owns a WindowTreeClient. // . Registers itself as the factory for creating NativeWidgets so that a // NativeWidgetMus is created.