1 // Copyright (c) 2013 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 "third_party/blink/renderer/core/clipboard/system_clipboard.h"
6 
7 #include "base/memory/scoped_refptr.h"
8 #include "build/build_config.h"
9 #include "mojo/public/cpp/system/platform_handle.h"
10 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
11 #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
12 #include "third_party/blink/public/platform/platform.h"
13 #include "third_party/blink/public/platform/web_drag_data.h"
14 #include "third_party/blink/public/platform/web_string.h"
15 #include "third_party/blink/renderer/core/clipboard/clipboard_mime_types.h"
16 #include "third_party/blink/renderer/core/clipboard/clipboard_utilities.h"
17 #include "third_party/blink/renderer/core/clipboard/data_object.h"
18 #include "third_party/blink/renderer/core/frame/local_frame.h"
19 #include "third_party/blink/renderer/platform/graphics/image.h"
20 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
21 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
22 #include "third_party/skia/include/core/SkBitmap.h"
23 
24 namespace blink {
25 
26 namespace {
27 
NonNullString(const String & string)28 String NonNullString(const String& string) {
29   return string.IsNull() ? g_empty_string16_bit : string;
30 }
31 
32 }  // namespace
33 
SystemClipboard(LocalFrame * frame)34 SystemClipboard::SystemClipboard(LocalFrame* frame) {
35   frame->GetBrowserInterfaceBroker().GetInterface(
36       clipboard_.BindNewPipeAndPassReceiver());
37 }
38 
IsSelectionMode() const39 bool SystemClipboard::IsSelectionMode() const {
40   return buffer_ == mojom::ClipboardBuffer::kSelection;
41 }
42 
SetSelectionMode(bool selection_mode)43 void SystemClipboard::SetSelectionMode(bool selection_mode) {
44   buffer_ = selection_mode ? mojom::ClipboardBuffer::kSelection
45                            : mojom::ClipboardBuffer::kStandard;
46 }
47 
CanSmartReplace()48 bool SystemClipboard::CanSmartReplace() {
49   if (!IsValidBufferType(buffer_))
50     return false;
51   bool result = false;
52   clipboard_->IsFormatAvailable(mojom::ClipboardFormat::kSmartPaste, buffer_,
53                                 &result);
54   return result;
55 }
56 
IsHTMLAvailable()57 bool SystemClipboard::IsHTMLAvailable() {
58   if (!IsValidBufferType(buffer_))
59     return false;
60   bool result = false;
61   clipboard_->IsFormatAvailable(mojom::ClipboardFormat::kHtml, buffer_,
62                                 &result);
63   return result;
64 }
65 
SequenceNumber()66 uint64_t SystemClipboard::SequenceNumber() {
67   if (!IsValidBufferType(buffer_))
68     return 0;
69   uint64_t result = 0;
70   clipboard_->GetSequenceNumber(buffer_, &result);
71   return result;
72 }
73 
ReadAvailableTypes()74 Vector<String> SystemClipboard::ReadAvailableTypes() {
75   if (!IsValidBufferType(buffer_))
76     return {};
77   Vector<String> types;
78   bool contains_filenames;  // Unused argument.
79   clipboard_->ReadAvailableTypes(buffer_, &types, &contains_filenames);
80   return types;
81 }
82 
ReadPlainText()83 String SystemClipboard::ReadPlainText() {
84   return ReadPlainText(buffer_);
85 }
86 
ReadPlainText(mojom::ClipboardBuffer buffer)87 String SystemClipboard::ReadPlainText(mojom::ClipboardBuffer buffer) {
88   if (!IsValidBufferType(buffer))
89     return String();
90   String text;
91   clipboard_->ReadText(buffer, &text);
92   return text;
93 }
94 
WritePlainText(const String & plain_text,SmartReplaceOption)95 void SystemClipboard::WritePlainText(const String& plain_text,
96                                              SmartReplaceOption) {
97   // TODO(https://crbug.com/106449): add support for smart replace, which is
98   // currently under-specified.
99   String text = plain_text;
100 #if defined(OS_WIN)
101   ReplaceNewlinesWithWindowsStyleNewlines(text);
102 #endif
103   clipboard_->WriteText(NonNullString(text));
104 }
105 
ReadHTML(KURL & url,unsigned & fragment_start,unsigned & fragment_end)106 String SystemClipboard::ReadHTML(KURL& url,
107                                  unsigned& fragment_start,
108                                  unsigned& fragment_end) {
109   String html;
110   if (IsValidBufferType(buffer_)) {
111     clipboard_->ReadHtml(buffer_, &html, &url,
112                          static_cast<uint32_t*>(&fragment_start),
113                          static_cast<uint32_t*>(&fragment_end));
114   }
115   if (html.IsEmpty()) {
116     url = KURL();
117     fragment_start = 0;
118     fragment_end = 0;
119   }
120   return html;
121 }
122 
WriteHTML(const String & markup,const KURL & document_url,const String & plain_text,SmartReplaceOption smart_replace_option)123 void SystemClipboard::WriteHTML(const String& markup,
124                                 const KURL& document_url,
125                                 const String& plain_text,
126                                 SmartReplaceOption smart_replace_option) {
127   String text = plain_text;
128 #if defined(OS_WIN)
129   ReplaceNewlinesWithWindowsStyleNewlines(text);
130 #endif
131   ReplaceNBSPWithSpace(text);
132 
133   clipboard_->WriteHtml(NonNullString(markup), document_url);
134   clipboard_->WriteText(NonNullString(text));
135   if (smart_replace_option == kCanSmartReplace)
136     clipboard_->WriteSmartPasteMarker();
137 }
138 
ReadRTF()139 String SystemClipboard::ReadRTF() {
140   if (!IsValidBufferType(buffer_))
141     return String();
142   String rtf;
143   clipboard_->ReadRtf(buffer_, &rtf);
144   return rtf;
145 }
146 
ReadImage(mojom::ClipboardBuffer buffer)147 SkBitmap SystemClipboard::ReadImage(mojom::ClipboardBuffer buffer) {
148   if (!IsValidBufferType(buffer))
149     return SkBitmap();
150   SkBitmap image;
151   clipboard_->ReadImage(buffer, &image);
152   return image;
153 }
154 
ReadImageAsImageMarkup(mojom::blink::ClipboardBuffer buffer)155 String SystemClipboard::ReadImageAsImageMarkup(
156     mojom::blink::ClipboardBuffer buffer) {
157   SkBitmap bitmap = ReadImage(buffer);
158   return BitmapToImageMarkup(bitmap);
159 }
160 
WriteImageWithTag(Image * image,const KURL & url,const String & title)161 void SystemClipboard::WriteImageWithTag(Image* image,
162                                                 const KURL& url,
163                                                 const String& title) {
164   DCHECK(image);
165 
166   PaintImage paint_image = image->PaintImageForCurrentFrame();
167   SkBitmap bitmap;
168   if (sk_sp<SkImage> sk_image = paint_image.GetSkImage())
169     sk_image->asLegacyBitmap(&bitmap);
170   clipboard_->WriteImage(bitmap);
171 
172   if (url.IsValid() && !url.IsEmpty()) {
173 #if !defined(OS_MACOSX)
174     // See http://crbug.com/838808: Not writing text/plain on Mac for
175     // consistency between platforms, and to help fix errors in applications
176     // which prefer text/plain content over image content for compatibility with
177     // Microsoft Word.
178     clipboard_->WriteBookmark(url.GetString(), NonNullString(title));
179 #endif
180 
181     // When writing the image, we also write the image markup so that pasting
182     // into rich text editors, such as Gmail, reveals the image. We also don't
183     // want to call writeText(), since some applications (WordPad) don't pick
184     // the image if there is also a text format on the clipboard.
185     clipboard_->WriteHtml(URLToImageMarkup(url, title), KURL());
186   }
187 }
188 
WriteImage(const SkBitmap & bitmap)189 void SystemClipboard::WriteImage(const SkBitmap& bitmap) {
190   clipboard_->WriteImage(bitmap);
191 }
192 
ReadCustomData(const String & type)193 String SystemClipboard::ReadCustomData(const String& type) {
194   if (!IsValidBufferType(buffer_))
195     return String();
196   String data;
197   clipboard_->ReadCustomData(buffer_, NonNullString(type), &data);
198   return data;
199 }
200 
WriteDataObject(DataObject * data_object)201 void SystemClipboard::WriteDataObject(DataObject* data_object) {
202   DCHECK(data_object);
203   // This plagiarizes the logic in DropDataBuilder::Build, but only extracts the
204   // data needed for the implementation of WriteDataObject.
205   //
206   // We avoid calling the WriteFoo functions if there is no data associated with
207   // a type. This prevents stomping on clipboard contents that might have been
208   // written by extension functions such as chrome.bookmarkManagerPrivate.copy.
209   //
210   // TODO(slangley): Use a mojo struct to send web_drag_data and allow receiving
211   // side to extract the data required.
212   // TODO(dcheng): Properly support text/uri-list here.
213 
214   HashMap<String, String> custom_data;
215   WebDragData data = data_object->ToWebDragData();
216   for (const WebDragData::Item& item : data.Items()) {
217     if (item.storage_type == WebDragData::Item::kStorageTypeString) {
218       if (item.string_type == kMimeTypeTextPlain) {
219         clipboard_->WriteText(NonNullString(item.string_data));
220       } else if (item.string_type == kMimeTypeTextHTML) {
221         clipboard_->WriteHtml(NonNullString(item.string_data), KURL());
222       } else if (item.string_type != kMimeTypeDownloadURL) {
223         custom_data.insert(item.string_type, NonNullString(item.string_data));
224       }
225     }
226   }
227   if (!custom_data.IsEmpty()) {
228     clipboard_->WriteCustomData(std::move(custom_data));
229   }
230 }
231 
CommitWrite()232 void SystemClipboard::CommitWrite() {
233   clipboard_->CommitWrite();
234 }
235 
IsValidBufferType(mojom::ClipboardBuffer buffer)236 bool SystemClipboard::IsValidBufferType(mojom::ClipboardBuffer buffer) {
237   switch (buffer) {
238     case mojom::ClipboardBuffer::kStandard:
239       return true;
240     case mojom::ClipboardBuffer::kSelection:
241       // mirroring ui/base/clipboard/clipboard.h
242 #if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
243       return true;
244 #else
245       // Chrome OS and non-X11 unix builds do not support
246       // the X selection clipboard.
247       // TODO(http://crbug.com/361753): remove the need for this case.
248       return false;
249 #endif
250   }
251   return true;
252 }
253 
254 }  // namespace blink
255