1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/base/clipboard/test/test_clipboard.h"
6 
7 #include <stddef.h>
8 #include <memory>
9 #include <utility>
10 #include "base/memory/ptr_util.h"
11 #include "base/numerics/safe_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "build/build_config.h"
14 #include "skia/ext/skia_utils_base.h"
15 #include "ui/base/clipboard/clipboard_constants.h"
16 #include "ui/base/clipboard/clipboard_monitor.h"
17 #include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
18 #include "ui/base/data_transfer_policy/data_transfer_policy_controller.h"
19 
20 namespace ui {
21 
22 namespace {
IsDataReadAllowed(const DataTransferEndpoint * src,const DataTransferEndpoint * dst)23 bool IsDataReadAllowed(const DataTransferEndpoint* src,
24                        const DataTransferEndpoint* dst) {
25   auto* policy_controller = DataTransferPolicyController::Get();
26   if (!policy_controller)
27     return true;
28   return policy_controller->IsDataReadAllowed(src, dst);
29 }
30 }  // namespace
31 
TestClipboard()32 TestClipboard::TestClipboard()
33     : default_store_buffer_(ClipboardBuffer::kCopyPaste) {}
34 
35 TestClipboard::~TestClipboard() = default;
36 
CreateForCurrentThread()37 TestClipboard* TestClipboard::CreateForCurrentThread() {
38   base::AutoLock lock(Clipboard::ClipboardMapLock());
39   auto* clipboard = new TestClipboard;
40   (*Clipboard::ClipboardMapPtr())[base::PlatformThread::CurrentId()] =
41       base::WrapUnique(clipboard);
42   return clipboard;
43 }
44 
SetLastModifiedTime(const base::Time & time)45 void TestClipboard::SetLastModifiedTime(const base::Time& time) {
46   last_modified_time_ = time;
47 }
48 
OnPreShutdown()49 void TestClipboard::OnPreShutdown() {}
50 
GetSequenceNumber(ClipboardBuffer buffer) const51 uint64_t TestClipboard::GetSequenceNumber(ClipboardBuffer buffer) const {
52   return GetStore(buffer).sequence_number;
53 }
54 
IsFormatAvailable(const ClipboardFormatType & format,ClipboardBuffer buffer,const ui::DataTransferEndpoint * data_dst) const55 bool TestClipboard::IsFormatAvailable(
56     const ClipboardFormatType& format,
57     ClipboardBuffer buffer,
58     const ui::DataTransferEndpoint* data_dst) const {
59   if (!IsDataReadAllowed(GetStore(buffer).data_src.get(), data_dst))
60     return false;
61 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
62   // The linux clipboard treats the presence of text on the clipboard
63   // as the url format being available.
64   if (format == ClipboardFormatType::GetUrlType())
65     return IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer,
66                              data_dst);
67 #endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
68   const DataStore& store = GetStore(buffer);
69   return base::Contains(store.data, format);
70 }
71 
Clear(ClipboardBuffer buffer)72 void TestClipboard::Clear(ClipboardBuffer buffer) {
73   GetStore(buffer).Clear();
74 }
75 
ReadAvailableTypes(ClipboardBuffer buffer,const DataTransferEndpoint * data_dst,std::vector<base::string16> * types) const76 void TestClipboard::ReadAvailableTypes(
77     ClipboardBuffer buffer,
78     const DataTransferEndpoint* data_dst,
79     std::vector<base::string16>* types) const {
80   DCHECK(types);
81   types->clear();
82   if (!IsDataReadAllowed(GetStore(buffer).data_src.get(), data_dst))
83     return;
84 
85   if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer,
86                         data_dst))
87     types->push_back(base::UTF8ToUTF16(kMimeTypeText));
88   if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst))
89     types->push_back(base::UTF8ToUTF16(kMimeTypeHTML));
90 
91   if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst))
92     types->push_back(base::UTF8ToUTF16(kMimeTypeRTF));
93   if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer, data_dst))
94     types->push_back(base::UTF8ToUTF16(kMimeTypePNG));
95 }
96 
97 std::vector<base::string16>
ReadAvailablePlatformSpecificFormatNames(ClipboardBuffer buffer,const ui::DataTransferEndpoint * data_dst) const98 TestClipboard::ReadAvailablePlatformSpecificFormatNames(
99     ClipboardBuffer buffer,
100     const ui::DataTransferEndpoint* data_dst) const {
101   const DataStore& store = GetStore(buffer);
102   if (!IsDataReadAllowed(store.data_src.get(), data_dst))
103     return {};
104 
105   const auto& data = store.data;
106   std::vector<base::string16> types;
107   types.reserve(data.size());
108   for (const auto& it : data)
109     types.push_back(base::UTF8ToUTF16(it.first.GetName()));
110 
111   // Some platforms add additional raw types to represent text, or offer them
112   // as available formats by automatically converting between them.
113   if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer,
114                         data_dst)) {
115 #if defined(USE_X11)
116     types.push_back(base::ASCIIToUTF16("TEXT"));
117     types.push_back(base::ASCIIToUTF16("STRING"));
118     types.push_back(base::ASCIIToUTF16("UTF8_STRING"));
119 #elif defined(OS_WIN)
120     types.push_back(base::ASCIIToUTF16("CF_OEMTEXT"));
121 #elif defined(OS_APPLE)
122     types.push_back(base::ASCIIToUTF16("NSStringPboardType"));
123 #endif
124   }
125 
126   return types;
127 }
128 
ReadText(ClipboardBuffer buffer,const DataTransferEndpoint * data_dst,base::string16 * result) const129 void TestClipboard::ReadText(ClipboardBuffer buffer,
130                              const DataTransferEndpoint* data_dst,
131                              base::string16* result) const {
132   if (!IsDataReadAllowed(GetStore(buffer).data_src.get(), data_dst))
133     return;
134 
135   std::string result8;
136   ReadAsciiText(buffer, data_dst, &result8);
137   *result = base::UTF8ToUTF16(result8);
138 }
139 
140 // TODO(crbug.com/1103215): |data_dst| should be supported.
ReadAsciiText(ClipboardBuffer buffer,const DataTransferEndpoint * data_dst,std::string * result) const141 void TestClipboard::ReadAsciiText(ClipboardBuffer buffer,
142                                   const DataTransferEndpoint* data_dst,
143                                   std::string* result) const {
144   const DataStore& store = GetStore(buffer);
145   if (!IsDataReadAllowed(store.data_src.get(), data_dst))
146     return;
147 
148   result->clear();
149   auto it = store.data.find(ClipboardFormatType::GetPlainTextType());
150   if (it != store.data.end())
151     *result = it->second;
152 }
153 
ReadHTML(ClipboardBuffer buffer,const DataTransferEndpoint * data_dst,base::string16 * markup,std::string * src_url,uint32_t * fragment_start,uint32_t * fragment_end) const154 void TestClipboard::ReadHTML(ClipboardBuffer buffer,
155                              const DataTransferEndpoint* data_dst,
156                              base::string16* markup,
157                              std::string* src_url,
158                              uint32_t* fragment_start,
159                              uint32_t* fragment_end) const {
160   const DataStore& store = GetStore(buffer);
161   if (!IsDataReadAllowed(store.data_src.get(), data_dst))
162     return;
163 
164   markup->clear();
165   src_url->clear();
166   auto it = store.data.find(ClipboardFormatType::GetHtmlType());
167   if (it != store.data.end())
168     *markup = base::UTF8ToUTF16(it->second);
169   *src_url = store.html_src_url;
170   *fragment_start = 0;
171   *fragment_end = base::checked_cast<uint32_t>(markup->size());
172 }
173 
ReadSvg(ClipboardBuffer buffer,const DataTransferEndpoint * data_dst,base::string16 * result) const174 void TestClipboard::ReadSvg(ClipboardBuffer buffer,
175                             const DataTransferEndpoint* data_dst,
176                             base::string16* result) const {
177   const DataStore& store = GetStore(buffer);
178   if (!IsDataReadAllowed(store.data_src.get(), data_dst))
179     return;
180 
181   result->clear();
182   auto it = store.data.find(ClipboardFormatType::GetSvgType());
183   if (it != store.data.end())
184     *result = base::UTF8ToUTF16(it->second);
185 }
186 
ReadRTF(ClipboardBuffer buffer,const DataTransferEndpoint * data_dst,std::string * result) const187 void TestClipboard::ReadRTF(ClipboardBuffer buffer,
188                             const DataTransferEndpoint* data_dst,
189                             std::string* result) const {
190   const DataStore& store = GetStore(buffer);
191   if (!IsDataReadAllowed(store.data_src.get(), data_dst))
192     return;
193 
194   result->clear();
195   auto it = store.data.find(ClipboardFormatType::GetRtfType());
196   if (it != store.data.end())
197     *result = it->second;
198 }
199 
ReadImage(ClipboardBuffer buffer,const DataTransferEndpoint * data_dst,ReadImageCallback callback) const200 void TestClipboard::ReadImage(ClipboardBuffer buffer,
201                               const DataTransferEndpoint* data_dst,
202                               ReadImageCallback callback) const {
203   const DataStore& store = GetStore(buffer);
204   if (!IsDataReadAllowed(store.data_src.get(), data_dst)) {
205     std::move(callback).Run(SkBitmap());
206     return;
207   }
208   std::move(callback).Run(store.image);
209 }
210 
211 // TODO(crbug.com/1103215): |data_dst| should be supported.
ReadCustomData(ClipboardBuffer buffer,const base::string16 & type,const DataTransferEndpoint * data_dst,base::string16 * result) const212 void TestClipboard::ReadCustomData(ClipboardBuffer buffer,
213                                    const base::string16& type,
214                                    const DataTransferEndpoint* data_dst,
215                                    base::string16* result) const {}
216 
217 // TODO(crbug.com/1103215): |data_dst| should be supported.
ReadBookmark(const DataTransferEndpoint * data_dst,base::string16 * title,std::string * url) const218 void TestClipboard::ReadBookmark(const DataTransferEndpoint* data_dst,
219                                  base::string16* title,
220                                  std::string* url) const {
221   const DataStore& store = GetDefaultStore();
222   if (!IsDataReadAllowed(store.data_src.get(), data_dst))
223     return;
224 
225   if (url) {
226     auto it = store.data.find(ClipboardFormatType::GetUrlType());
227     if (it != store.data.end())
228       *url = it->second;
229   }
230   if (title)
231     *title = base::UTF8ToUTF16(store.url_title);
232 }
233 
ReadData(const ClipboardFormatType & format,const DataTransferEndpoint * data_dst,std::string * result) const234 void TestClipboard::ReadData(const ClipboardFormatType& format,
235                              const DataTransferEndpoint* data_dst,
236                              std::string* result) const {
237   const DataStore& store = GetDefaultStore();
238   if (!IsDataReadAllowed(store.data_src.get(), data_dst))
239     return;
240 
241   result->clear();
242   auto it = store.data.find(format);
243   if (it != store.data.end())
244     *result = it->second;
245 }
246 
GetLastModifiedTime() const247 base::Time TestClipboard::GetLastModifiedTime() const {
248   return last_modified_time_;
249 }
250 
ClearLastModifiedTime()251 void TestClipboard::ClearLastModifiedTime() {
252   last_modified_time_ = base::Time();
253 }
254 
255 #if defined(USE_OZONE)
IsSelectionBufferAvailable() const256 bool TestClipboard::IsSelectionBufferAvailable() const {
257   return true;
258 }
259 #endif  // defined(USE_OZONE)
260 
WritePortableRepresentations(ClipboardBuffer buffer,const ObjectMap & objects,std::unique_ptr<DataTransferEndpoint> data_src)261 void TestClipboard::WritePortableRepresentations(
262     ClipboardBuffer buffer,
263     const ObjectMap& objects,
264     std::unique_ptr<DataTransferEndpoint> data_src) {
265   Clear(buffer);
266   default_store_buffer_ = buffer;
267   for (const auto& kv : objects)
268     DispatchPortableRepresentation(kv.first, kv.second);
269   default_store_buffer_ = ClipboardBuffer::kCopyPaste;
270   GetStore(buffer).SetDataSource(std::move(data_src));
271 }
272 
WritePlatformRepresentations(ClipboardBuffer buffer,std::vector<Clipboard::PlatformRepresentation> platform_representations,std::unique_ptr<DataTransferEndpoint> data_src)273 void TestClipboard::WritePlatformRepresentations(
274     ClipboardBuffer buffer,
275     std::vector<Clipboard::PlatformRepresentation> platform_representations,
276     std::unique_ptr<DataTransferEndpoint> data_src) {
277   Clear(buffer);
278   default_store_buffer_ = buffer;
279   DispatchPlatformRepresentations(std::move(platform_representations));
280   default_store_buffer_ = ClipboardBuffer::kCopyPaste;
281   GetStore(buffer).SetDataSource(std::move(data_src));
282 }
283 
WriteText(const char * text_data,size_t text_len)284 void TestClipboard::WriteText(const char* text_data, size_t text_len) {
285   std::string text(text_data, text_len);
286   GetDefaultStore().data[ClipboardFormatType::GetPlainTextType()] = text;
287 #if defined(OS_WIN)
288   // Create a dummy entry.
289   GetDefaultStore().data[ClipboardFormatType::GetPlainTextAType()];
290 #endif
291   if (IsSupportedClipboardBuffer(ClipboardBuffer::kSelection))
292     GetStore(ClipboardBuffer::kSelection)
293         .data[ClipboardFormatType::GetPlainTextType()] = text;
294   ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged();
295 }
296 
WriteHTML(const char * markup_data,size_t markup_len,const char * url_data,size_t url_len)297 void TestClipboard::WriteHTML(const char* markup_data,
298                               size_t markup_len,
299                               const char* url_data,
300                               size_t url_len) {
301   base::string16 markup;
302   base::UTF8ToUTF16(markup_data, markup_len, &markup);
303   GetDefaultStore().data[ClipboardFormatType::GetHtmlType()] =
304       base::UTF16ToUTF8(markup);
305   GetDefaultStore().html_src_url = std::string(url_data, url_len);
306 }
307 
WriteSvg(const char * markup_data,size_t markup_len)308 void TestClipboard::WriteSvg(const char* markup_data, size_t markup_len) {
309   base::string16 markup;
310   base::UTF8ToUTF16(markup_data, markup_len, &markup);
311   GetDefaultStore().data[ClipboardFormatType::GetSvgType()] =
312       base::UTF16ToUTF8(markup);
313 }
314 
WriteRTF(const char * rtf_data,size_t data_len)315 void TestClipboard::WriteRTF(const char* rtf_data, size_t data_len) {
316   GetDefaultStore().data[ClipboardFormatType::GetRtfType()] =
317       std::string(rtf_data, data_len);
318 }
319 
WriteBookmark(const char * title_data,size_t title_len,const char * url_data,size_t url_len)320 void TestClipboard::WriteBookmark(const char* title_data,
321                                   size_t title_len,
322                                   const char* url_data,
323                                   size_t url_len) {
324   GetDefaultStore().data[ClipboardFormatType::GetUrlType()] =
325       std::string(url_data, url_len);
326   GetDefaultStore().url_title = std::string(title_data, title_len);
327 }
328 
WriteWebSmartPaste()329 void TestClipboard::WriteWebSmartPaste() {
330   // Create a dummy entry.
331   GetDefaultStore().data[ClipboardFormatType::GetWebKitSmartPasteType()];
332 }
333 
WriteBitmap(const SkBitmap & bitmap)334 void TestClipboard::WriteBitmap(const SkBitmap& bitmap) {
335   // We expect callers to sanitize `bitmap` to be N32 color type, to avoid
336   // out-of-bounds issues due to unexpected bits-per-pixel while copying the
337   // bitmap's pixel buffer. This DCHECK is to help alert us if we've missed
338   // something.
339   DCHECK_EQ(bitmap.colorType(), kN32_SkColorType);
340 
341   // Create a dummy entry.
342   GetDefaultStore().data[ClipboardFormatType::GetBitmapType()];
343   GetDefaultStore().image = bitmap;
344   ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged();
345 }
346 
WriteData(const ClipboardFormatType & format,const char * data_data,size_t data_len)347 void TestClipboard::WriteData(const ClipboardFormatType& format,
348                               const char* data_data,
349                               size_t data_len) {
350   GetDefaultStore().data[format] = std::string(data_data, data_len);
351 }
352 
353 TestClipboard::DataStore::DataStore() = default;
354 
DataStore(const DataStore & other)355 TestClipboard::DataStore::DataStore(const DataStore& other) {
356   sequence_number = other.sequence_number;
357   data = other.data;
358   url_title = other.url_title;
359   html_src_url = other.html_src_url;
360   image = other.image;
361   data_src = other.data_src ? std::make_unique<DataTransferEndpoint>(
362                                   DataTransferEndpoint(*(other.data_src)))
363                             : nullptr;
364 }
365 
operator =(const DataStore & other)366 TestClipboard::DataStore& TestClipboard::DataStore::operator=(
367     const DataStore& other) {
368   sequence_number = other.sequence_number;
369   data = other.data;
370   url_title = other.url_title;
371   html_src_url = other.html_src_url;
372   image = other.image;
373   data_src = other.data_src ? std::make_unique<DataTransferEndpoint>(
374                                   DataTransferEndpoint(*(other.data_src)))
375                             : nullptr;
376   return *this;
377 }
378 
379 TestClipboard::DataStore::~DataStore() = default;
380 
Clear()381 void TestClipboard::DataStore::Clear() {
382   data.clear();
383   url_title.clear();
384   html_src_url.clear();
385   image = SkBitmap();
386 }
387 
SetDataSource(std::unique_ptr<DataTransferEndpoint> data_src)388 void TestClipboard::DataStore::SetDataSource(
389     std::unique_ptr<DataTransferEndpoint> data_src) {
390   this->data_src = std::move(data_src);
391 }
392 
GetStore(ClipboardBuffer buffer) const393 const TestClipboard::DataStore& TestClipboard::GetStore(
394     ClipboardBuffer buffer) const {
395   CHECK(IsSupportedClipboardBuffer(buffer));
396   return stores_[buffer];
397 }
398 
GetStore(ClipboardBuffer buffer)399 TestClipboard::DataStore& TestClipboard::GetStore(ClipboardBuffer buffer) {
400   CHECK(IsSupportedClipboardBuffer(buffer));
401   DataStore& store = stores_[buffer];
402   ++store.sequence_number;
403   return store;
404 }
405 
GetDefaultStore() const406 const TestClipboard::DataStore& TestClipboard::GetDefaultStore() const {
407   return GetStore(default_store_buffer_);
408 }
409 
GetDefaultStore()410 TestClipboard::DataStore& TestClipboard::GetDefaultStore() {
411   return GetStore(default_store_buffer_);
412 }
413 
414 }  // namespace ui
415