| // Copyright 2014 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/base/clipboard/test/test_clipboard.h" |
| |
| #include <stddef.h> |
| #include <memory> |
| #include <utility> |
| #include "base/memory/ptr_util.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "skia/ext/skia_utils_base.h" |
| #include "ui/base/clipboard/clipboard_constants.h" |
| #include "ui/base/clipboard/clipboard_data_endpoint.h" |
| #include "ui/base/clipboard/clipboard_dlp_controller.h" |
| #include "ui/base/clipboard/clipboard_monitor.h" |
| |
| namespace ui { |
| |
| TestClipboard::TestClipboard() |
| : default_store_buffer_(ClipboardBuffer::kCopyPaste) {} |
| |
| TestClipboard::~TestClipboard() = default; |
| |
| TestClipboard* TestClipboard::CreateForCurrentThread() { |
| base::AutoLock lock(Clipboard::ClipboardMapLock()); |
| auto* clipboard = new TestClipboard; |
| (*Clipboard::ClipboardMapPtr())[base::PlatformThread::CurrentId()] = |
| base::WrapUnique(clipboard); |
| return clipboard; |
| } |
| |
| void TestClipboard::SetLastModifiedTime(const base::Time& time) { |
| last_modified_time_ = time; |
| } |
| |
| void TestClipboard::OnPreShutdown() {} |
| |
| uint64_t TestClipboard::GetSequenceNumber(ClipboardBuffer buffer) const { |
| return GetStore(buffer).sequence_number; |
| } |
| |
| void TestClipboard::SetClipboardDlpController( |
| std::unique_ptr<ClipboardDlpController> dlp_controller) { |
| dlp_controller_ = std::move(dlp_controller); |
| } |
| |
| bool TestClipboard::IsFormatAvailable( |
| const ClipboardFormatType& format, |
| ClipboardBuffer buffer, |
| const ui::ClipboardDataEndpoint* data_dst) const { |
| if (dlp_controller_ && !dlp_controller_->IsDataReadAllowed( |
| GetStore(buffer).data_src.get(), data_dst)) |
| return false; |
| #if defined(OS_LINUX) |
| // The linux clipboard treats the presence of text on the clipboard |
| // as the url format being available. |
| if (format == ClipboardFormatType::GetUrlType()) |
| return IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer, |
| data_dst); |
| #endif // OS_LINUX |
| const DataStore& store = GetStore(buffer); |
| return base::Contains(store.data, format); |
| } |
| |
| void TestClipboard::Clear(ClipboardBuffer buffer) { |
| GetStore(buffer).Clear(); |
| } |
| |
| void TestClipboard::ReadAvailableTypes( |
| ClipboardBuffer buffer, |
| const ClipboardDataEndpoint* data_dst, |
| std::vector<base::string16>* types) const { |
| DCHECK(types); |
| types->clear(); |
| if (dlp_controller_ && !dlp_controller_->IsDataReadAllowed( |
| GetStore(buffer).data_src.get(), data_dst)) |
| return; |
| |
| if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer, |
| data_dst)) |
| types->push_back(base::UTF8ToUTF16(kMimeTypeText)); |
| if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst)) |
| types->push_back(base::UTF8ToUTF16(kMimeTypeHTML)); |
| |
| if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst)) |
| types->push_back(base::UTF8ToUTF16(kMimeTypeRTF)); |
| if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer, data_dst)) |
| types->push_back(base::UTF8ToUTF16(kMimeTypePNG)); |
| } |
| |
| std::vector<base::string16> |
| TestClipboard::ReadAvailablePlatformSpecificFormatNames( |
| ClipboardBuffer buffer, |
| const ui::ClipboardDataEndpoint* data_dst) const { |
| const DataStore& store = GetStore(buffer); |
| if (dlp_controller_ && |
| !dlp_controller_->IsDataReadAllowed(store.data_src.get(), data_dst)) |
| return {}; |
| |
| const auto& data = store.data; |
| std::vector<base::string16> types; |
| types.reserve(data.size()); |
| for (const auto& it : data) |
| types.push_back(base::UTF8ToUTF16(it.first.GetName())); |
| |
| // Some platforms add additional raw types to represent text, or offer them |
| // as available formats by automatically converting between them. |
| if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer, |
| data_dst)) { |
| #if defined(USE_X11) |
| types.push_back(base::ASCIIToUTF16("TEXT")); |
| types.push_back(base::ASCIIToUTF16("STRING")); |
| types.push_back(base::ASCIIToUTF16("UTF8_STRING")); |
| #elif defined(OS_WIN) |
| types.push_back(base::ASCIIToUTF16("CF_LOCALE")); |
| types.push_back(base::ASCIIToUTF16("CF_OEMTEXT")); |
| #elif defined(OS_APPLE) |
| types.push_back(base::ASCIIToUTF16("NSStringPboardType")); |
| #endif |
| } |
| |
| return types; |
| } |
| |
| void TestClipboard::ReadText(ClipboardBuffer buffer, |
| const ClipboardDataEndpoint* data_dst, |
| base::string16* result) const { |
| if (dlp_controller_ && !dlp_controller_->IsDataReadAllowed( |
| GetStore(buffer).data_src.get(), data_dst)) |
| return; |
| |
| std::string result8; |
| ReadAsciiText(buffer, data_dst, &result8); |
| *result = base::UTF8ToUTF16(result8); |
| } |
| |
| // TODO(crbug.com/1103215): |data_dst| should be supported. |
| void TestClipboard::ReadAsciiText(ClipboardBuffer buffer, |
| const ClipboardDataEndpoint* data_dst, |
| std::string* result) const { |
| const DataStore& store = GetStore(buffer); |
| if (dlp_controller_ && |
| !dlp_controller_->IsDataReadAllowed(store.data_src.get(), data_dst)) |
| return; |
| |
| result->clear(); |
| auto it = store.data.find(ClipboardFormatType::GetPlainTextType()); |
| if (it != store.data.end()) |
| *result = it->second; |
| } |
| |
| void TestClipboard::ReadHTML(ClipboardBuffer buffer, |
| const ClipboardDataEndpoint* data_dst, |
| base::string16* markup, |
| std::string* src_url, |
| uint32_t* fragment_start, |
| uint32_t* fragment_end) const { |
| const DataStore& store = GetStore(buffer); |
| if (dlp_controller_ && |
| !dlp_controller_->IsDataReadAllowed(store.data_src.get(), data_dst)) |
| return; |
| |
| markup->clear(); |
| src_url->clear(); |
| auto it = store.data.find(ClipboardFormatType::GetHtmlType()); |
| if (it != store.data.end()) |
| *markup = base::UTF8ToUTF16(it->second); |
| *src_url = store.html_src_url; |
| *fragment_start = 0; |
| *fragment_end = base::checked_cast<uint32_t>(markup->size()); |
| } |
| |
| void TestClipboard::ReadRTF(ClipboardBuffer buffer, |
| const ClipboardDataEndpoint* data_dst, |
| std::string* result) const { |
| const DataStore& store = GetStore(buffer); |
| if (dlp_controller_ && |
| !dlp_controller_->IsDataReadAllowed(store.data_src.get(), data_dst)) |
| return; |
| |
| result->clear(); |
| auto it = store.data.find(ClipboardFormatType::GetRtfType()); |
| if (it != store.data.end()) |
| *result = it->second; |
| } |
| |
| void TestClipboard::ReadImage(ClipboardBuffer buffer, |
| const ClipboardDataEndpoint* data_dst, |
| ReadImageCallback callback) const { |
| std::move(callback).Run(GetStore(buffer).image); |
| } |
| |
| // TODO(crbug.com/1103215): |data_dst| should be supported. |
| void TestClipboard::ReadCustomData(ClipboardBuffer buffer, |
| const base::string16& type, |
| const ClipboardDataEndpoint* data_dst, |
| base::string16* result) const {} |
| |
| // TODO(crbug.com/1103215): |data_dst| should be supported. |
| void TestClipboard::ReadBookmark(const ClipboardDataEndpoint* data_dst, |
| base::string16* title, |
| std::string* url) const { |
| const DataStore& store = GetDefaultStore(); |
| if (dlp_controller_ && |
| !dlp_controller_->IsDataReadAllowed(store.data_src.get(), data_dst)) |
| return; |
| |
| if (url) { |
| auto it = store.data.find(ClipboardFormatType::GetUrlType()); |
| if (it != store.data.end()) |
| *url = it->second; |
| } |
| if (title) |
| *title = base::UTF8ToUTF16(store.url_title); |
| } |
| |
| void TestClipboard::ReadData(const ClipboardFormatType& format, |
| const ClipboardDataEndpoint* data_dst, |
| std::string* result) const { |
| const DataStore& store = GetDefaultStore(); |
| if (dlp_controller_ && |
| !dlp_controller_->IsDataReadAllowed(store.data_src.get(), data_dst)) |
| return; |
| |
| result->clear(); |
| auto it = store.data.find(format); |
| if (it != store.data.end()) |
| *result = it->second; |
| } |
| |
| base::Time TestClipboard::GetLastModifiedTime() const { |
| return last_modified_time_; |
| } |
| |
| void TestClipboard::ClearLastModifiedTime() { |
| last_modified_time_ = base::Time(); |
| } |
| |
| #if defined(USE_OZONE) |
| bool TestClipboard::IsSelectionBufferAvailable() const { |
| return false; |
| } |
| #endif // defined(USE_OZONE) |
| |
| void TestClipboard::WritePortableRepresentations( |
| ClipboardBuffer buffer, |
| const ObjectMap& objects, |
| std::unique_ptr<ClipboardDataEndpoint> data_src) { |
| Clear(buffer); |
| default_store_buffer_ = buffer; |
| for (const auto& kv : objects) |
| DispatchPortableRepresentation(kv.first, kv.second); |
| default_store_buffer_ = ClipboardBuffer::kCopyPaste; |
| GetStore(buffer).SetDataSource(std::move(data_src)); |
| } |
| |
| void TestClipboard::WritePlatformRepresentations( |
| ClipboardBuffer buffer, |
| std::vector<Clipboard::PlatformRepresentation> platform_representations, |
| std::unique_ptr<ClipboardDataEndpoint> data_src) { |
| Clear(buffer); |
| default_store_buffer_ = buffer; |
| DispatchPlatformRepresentations(std::move(platform_representations)); |
| default_store_buffer_ = ClipboardBuffer::kCopyPaste; |
| GetStore(buffer).SetDataSource(std::move(data_src)); |
| } |
| |
| void TestClipboard::WriteText(const char* text_data, size_t text_len) { |
| std::string text(text_data, text_len); |
| GetDefaultStore().data[ClipboardFormatType::GetPlainTextType()] = text; |
| #if defined(OS_WIN) |
| // Create a dummy entry. |
| GetDefaultStore().data[ClipboardFormatType::GetPlainTextAType()]; |
| #endif |
| if (IsSupportedClipboardBuffer(ClipboardBuffer::kSelection)) |
| GetStore(ClipboardBuffer::kSelection) |
| .data[ClipboardFormatType::GetPlainTextType()] = text; |
| ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged(); |
| } |
| |
| void TestClipboard::WriteHTML(const char* markup_data, |
| size_t markup_len, |
| const char* url_data, |
| size_t url_len) { |
| base::string16 markup; |
| base::UTF8ToUTF16(markup_data, markup_len, &markup); |
| GetDefaultStore().data[ClipboardFormatType::GetHtmlType()] = |
| base::UTF16ToUTF8(markup); |
| GetDefaultStore().html_src_url = std::string(url_data, url_len); |
| } |
| |
| void TestClipboard::WriteRTF(const char* rtf_data, size_t data_len) { |
| GetDefaultStore().data[ClipboardFormatType::GetRtfType()] = |
| std::string(rtf_data, data_len); |
| } |
| |
| void TestClipboard::WriteBookmark(const char* title_data, |
| size_t title_len, |
| const char* url_data, |
| size_t url_len) { |
| GetDefaultStore().data[ClipboardFormatType::GetUrlType()] = |
| std::string(url_data, url_len); |
| GetDefaultStore().url_title = std::string(title_data, title_len); |
| } |
| |
| void TestClipboard::WriteWebSmartPaste() { |
| // Create a dummy entry. |
| GetDefaultStore().data[ClipboardFormatType::GetWebKitSmartPasteType()]; |
| } |
| |
| void TestClipboard::WriteBitmap(const SkBitmap& bitmap) { |
| // Create a dummy entry. |
| GetDefaultStore().data[ClipboardFormatType::GetBitmapType()]; |
| SkBitmap& dst = GetDefaultStore().image; |
| // Either points bitmap at in_bitmap, or allocates and converts pixels. |
| if (!skia::SkBitmapToN32OpaqueOrPremul(bitmap, &dst)) { |
| NOTREACHED() << "Unable to convert bitmap for clipboard"; |
| return; |
| } |
| ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged(); |
| } |
| |
| void TestClipboard::WriteData(const ClipboardFormatType& format, |
| const char* data_data, |
| size_t data_len) { |
| GetDefaultStore().data[format] = std::string(data_data, data_len); |
| } |
| |
| TestClipboard::DataStore::DataStore() = default; |
| |
| TestClipboard::DataStore::DataStore(const DataStore& other) { |
| sequence_number = other.sequence_number; |
| data = other.data; |
| url_title = other.url_title; |
| html_src_url = other.html_src_url; |
| image = other.image; |
| data_src = other.data_src ? std::make_unique<ClipboardDataEndpoint>( |
| ClipboardDataEndpoint(*(other.data_src))) |
| : nullptr; |
| } |
| |
| TestClipboard::DataStore& TestClipboard::DataStore::operator=( |
| const DataStore& other) { |
| sequence_number = other.sequence_number; |
| data = other.data; |
| url_title = other.url_title; |
| html_src_url = other.html_src_url; |
| image = other.image; |
| data_src = other.data_src ? std::make_unique<ClipboardDataEndpoint>( |
| ClipboardDataEndpoint(*(other.data_src))) |
| : nullptr; |
| return *this; |
| } |
| |
| TestClipboard::DataStore::~DataStore() = default; |
| |
| void TestClipboard::DataStore::Clear() { |
| data.clear(); |
| url_title.clear(); |
| html_src_url.clear(); |
| image = SkBitmap(); |
| } |
| |
| void TestClipboard::DataStore::SetDataSource( |
| std::unique_ptr<ClipboardDataEndpoint> data_src) { |
| this->data_src = std::move(data_src); |
| } |
| |
| const TestClipboard::DataStore& TestClipboard::GetStore( |
| ClipboardBuffer buffer) const { |
| CHECK(IsSupportedClipboardBuffer(buffer)); |
| return stores_[buffer]; |
| } |
| |
| TestClipboard::DataStore& TestClipboard::GetStore(ClipboardBuffer buffer) { |
| CHECK(IsSupportedClipboardBuffer(buffer)); |
| DataStore& store = stores_[buffer]; |
| ++store.sequence_number; |
| return store; |
| } |
| |
| const TestClipboard::DataStore& TestClipboard::GetDefaultStore() const { |
| return GetStore(default_store_buffer_); |
| } |
| |
| TestClipboard::DataStore& TestClipboard::GetDefaultStore() { |
| return GetStore(default_store_buffer_); |
| } |
| |
| } // namespace ui |