| // Copyright (c) 2013 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 "third_party/blink/renderer/core/clipboard/system_clipboard.h" |
| |
| #include "base/memory/scoped_refptr.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "mojo/public/cpp/system/platform_handle.h" |
| #include "skia/ext/skia_utils_base.h" |
| #include "third_party/blink/public/common/browser_interface_broker_proxy.h" |
| #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" |
| #include "third_party/blink/public/platform/platform.h" |
| #include "third_party/blink/public/platform/web_drag_data.h" |
| #include "third_party/blink/public/platform/web_string.h" |
| #include "third_party/blink/public/web/web_local_frame.h" |
| #include "third_party/blink/public/web/web_local_frame_client.h" |
| #include "third_party/blink/renderer/core/clipboard/clipboard_mime_types.h" |
| #include "third_party/blink/renderer/core/clipboard/clipboard_utilities.h" |
| #include "third_party/blink/renderer/core/clipboard/data_object.h" |
| #include "third_party/blink/renderer/core/frame/local_dom_window.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_client.h" |
| #include "third_party/blink/renderer/core/frame/settings.h" |
| #include "third_party/blink/renderer/platform/graphics/image.h" |
| #include "third_party/blink/renderer/platform/weborigin/kurl.h" |
| #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| String NonNullString(const String& string) { |
| return string.IsNull() ? g_empty_string16_bit : string; |
| } |
| |
| } // namespace |
| |
| SystemClipboard::SystemClipboard(LocalFrame* frame) |
| : clipboard_(frame->DomWindow()) { |
| frame->GetBrowserInterfaceBroker().GetInterface( |
| clipboard_.BindNewPipeAndPassReceiver( |
| frame->GetTaskRunner(TaskType::kUserInteraction))); |
| #if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) |
| is_selection_buffer_available_ = |
| frame->GetSettings()->GetSelectionClipboardBufferAvailable(); |
| #endif // defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) |
| } |
| |
| bool SystemClipboard::IsSelectionMode() const { |
| return buffer_ == mojom::ClipboardBuffer::kSelection; |
| } |
| |
| void SystemClipboard::SetSelectionMode(bool selection_mode) { |
| buffer_ = selection_mode ? mojom::ClipboardBuffer::kSelection |
| : mojom::ClipboardBuffer::kStandard; |
| } |
| |
| bool SystemClipboard::CanSmartReplace() { |
| if (!IsValidBufferType(buffer_) || !clipboard_.is_bound()) |
| return false; |
| bool result = false; |
| clipboard_->IsFormatAvailable(mojom::ClipboardFormat::kSmartPaste, buffer_, |
| &result); |
| return result; |
| } |
| |
| bool SystemClipboard::IsHTMLAvailable() { |
| if (!IsValidBufferType(buffer_) || !clipboard_.is_bound()) |
| return false; |
| bool result = false; |
| clipboard_->IsFormatAvailable(mojom::ClipboardFormat::kHtml, buffer_, |
| &result); |
| return result; |
| } |
| |
| uint64_t SystemClipboard::SequenceNumber() { |
| if (!IsValidBufferType(buffer_) || !clipboard_.is_bound()) |
| return 0; |
| uint64_t result = 0; |
| clipboard_->GetSequenceNumber(buffer_, &result); |
| return result; |
| } |
| |
| Vector<String> SystemClipboard::ReadAvailableTypes() { |
| if (!IsValidBufferType(buffer_) || !clipboard_.is_bound()) |
| return {}; |
| Vector<String> types; |
| clipboard_->ReadAvailableTypes(buffer_, &types); |
| return types; |
| } |
| |
| String SystemClipboard::ReadPlainText() { |
| return ReadPlainText(buffer_); |
| } |
| |
| String SystemClipboard::ReadPlainText(mojom::ClipboardBuffer buffer) { |
| if (!IsValidBufferType(buffer) || !clipboard_.is_bound()) |
| return String(); |
| String text; |
| clipboard_->ReadText(buffer, &text); |
| return text; |
| } |
| |
| void SystemClipboard::WritePlainText(const String& plain_text, |
| SmartReplaceOption) { |
| // TODO(https://crbug.com/106449): add support for smart replace, which is |
| // currently under-specified. |
| String text = plain_text; |
| #if defined(OS_WIN) |
| ReplaceNewlinesWithWindowsStyleNewlines(text); |
| #endif |
| clipboard_->WriteText(NonNullString(text)); |
| } |
| |
| String SystemClipboard::ReadHTML(KURL& url, |
| unsigned& fragment_start, |
| unsigned& fragment_end) { |
| String html; |
| if (IsValidBufferType(buffer_)) { |
| clipboard_->ReadHtml(buffer_, &html, &url, |
| static_cast<uint32_t*>(&fragment_start), |
| static_cast<uint32_t*>(&fragment_end)); |
| } |
| if (html.IsEmpty()) { |
| url = KURL(); |
| fragment_start = 0; |
| fragment_end = 0; |
| } |
| return html; |
| } |
| |
| void SystemClipboard::WriteHTML(const String& markup, |
| const KURL& document_url, |
| SmartReplaceOption smart_replace_option) { |
| clipboard_->WriteHtml(NonNullString(markup), document_url); |
| if (smart_replace_option == kCanSmartReplace) |
| clipboard_->WriteSmartPasteMarker(); |
| } |
| |
| void SystemClipboard::ReadSvg( |
| mojom::blink::ClipboardHost::ReadSvgCallback callback) { |
| if (!IsValidBufferType(buffer_) || !clipboard_.is_bound()) { |
| std::move(callback).Run(String()); |
| return; |
| } |
| clipboard_->ReadSvg(buffer_, std::move(callback)); |
| } |
| |
| void SystemClipboard::WriteSvg(const String& markup) { |
| clipboard_->WriteSvg(NonNullString(markup)); |
| } |
| |
| String SystemClipboard::ReadRTF() { |
| if (!IsValidBufferType(buffer_) || !clipboard_.is_bound()) |
| return String(); |
| String rtf; |
| clipboard_->ReadRtf(buffer_, &rtf); |
| return rtf; |
| } |
| |
| SkBitmap SystemClipboard::ReadImage(mojom::ClipboardBuffer buffer) { |
| if (!IsValidBufferType(buffer) || !clipboard_.is_bound()) |
| return SkBitmap(); |
| SkBitmap image; |
| clipboard_->ReadImage(buffer, &image); |
| return image; |
| } |
| |
| String SystemClipboard::ReadImageAsImageMarkup( |
| mojom::blink::ClipboardBuffer buffer) { |
| SkBitmap bitmap = ReadImage(buffer); |
| return BitmapToImageMarkup(bitmap); |
| } |
| |
| void SystemClipboard::WriteImageWithTag(Image* image, |
| const KURL& url, |
| const String& title) { |
| DCHECK(image); |
| |
| PaintImage paint_image = image->PaintImageForCurrentFrame(); |
| SkBitmap bitmap; |
| if (sk_sp<SkImage> sk_image = paint_image.GetSwSkImage()) |
| sk_image->asLegacyBitmap(&bitmap); |
| // The bitmap backing a canvas can be in non-native skia pixel order (aka |
| // RGBA when kN32_SkColorType is BGRA-ordered, or higher bit-depth color-types |
| // like F16. The IPC to the browser requires the bitmap to be in N32 format |
| // so we convert it here if needed. |
| SkBitmap n32_bitmap; |
| if (skia::SkBitmapToN32OpaqueOrPremul(bitmap, &n32_bitmap)) |
| clipboard_->WriteImage(n32_bitmap); |
| else |
| clipboard_->WriteImage(SkBitmap()); |
| |
| if (url.IsValid() && !url.IsEmpty()) { |
| #if !defined(OS_MAC) |
| // See http://crbug.com/838808: Not writing text/plain on Mac for |
| // consistency between platforms, and to help fix errors in applications |
| // which prefer text/plain content over image content for compatibility with |
| // Microsoft Word. |
| clipboard_->WriteBookmark(url.GetString(), NonNullString(title)); |
| #endif |
| |
| // When writing the image, we also write the image markup so that pasting |
| // into rich text editors, such as Gmail, reveals the image. We also don't |
| // want to call writeText(), since some applications (WordPad) don't pick |
| // the image if there is also a text format on the clipboard. |
| clipboard_->WriteHtml(URLToImageMarkup(url, title), KURL()); |
| } |
| } |
| |
| void SystemClipboard::WriteImage(const SkBitmap& bitmap) { |
| clipboard_->WriteImage(bitmap); |
| } |
| |
| mojom::blink::ClipboardFilesPtr SystemClipboard::ReadFiles() { |
| mojom::blink::ClipboardFilesPtr files; |
| if (!IsValidBufferType(buffer_) || !clipboard_.is_bound()) |
| return files; |
| clipboard_->ReadFiles(buffer_, &files); |
| return files; |
| } |
| |
| String SystemClipboard::ReadCustomData(const String& type) { |
| if (!IsValidBufferType(buffer_) || !clipboard_.is_bound()) |
| return String(); |
| String data; |
| clipboard_->ReadCustomData(buffer_, NonNullString(type), &data); |
| return data; |
| } |
| |
| void SystemClipboard::WriteDataObject(DataObject* data_object) { |
| DCHECK(data_object); |
| // This plagiarizes the logic in DropDataBuilder::Build, but only extracts the |
| // data needed for the implementation of WriteDataObject. |
| // |
| // We avoid calling the WriteFoo functions if there is no data associated with |
| // a type. This prevents stomping on clipboard contents that might have been |
| // written by extension functions such as chrome.bookmarkManagerPrivate.copy. |
| // |
| // TODO(slangley): Use a mojo struct to send web_drag_data and allow receiving |
| // side to extract the data required. |
| // TODO(dcheng): Properly support text/uri-list here. |
| |
| HashMap<String, String> custom_data; |
| WebDragData data = data_object->ToWebDragData(); |
| for (const WebDragData::Item& item : data.Items()) { |
| if (item.storage_type == WebDragData::Item::kStorageTypeString) { |
| if (item.string_type == kMimeTypeTextPlain) { |
| clipboard_->WriteText(NonNullString(item.string_data)); |
| } else if (item.string_type == kMimeTypeTextHTML) { |
| clipboard_->WriteHtml(NonNullString(item.string_data), KURL()); |
| } else if (item.string_type != kMimeTypeDownloadURL) { |
| custom_data.insert(item.string_type, NonNullString(item.string_data)); |
| } |
| } |
| } |
| if (!custom_data.IsEmpty()) { |
| clipboard_->WriteCustomData(std::move(custom_data)); |
| } |
| } |
| |
| void SystemClipboard::CommitWrite() { |
| clipboard_->CommitWrite(); |
| } |
| |
| void SystemClipboard::CopyToFindPboard(const String& text) { |
| #if defined(OS_MAC) |
| clipboard_->WriteStringToFindPboard(text); |
| #endif |
| } |
| |
| void SystemClipboard::Trace(Visitor* visitor) const { |
| visitor->Trace(clipboard_); |
| } |
| |
| bool SystemClipboard::IsValidBufferType(mojom::ClipboardBuffer buffer) { |
| switch (buffer) { |
| case mojom::ClipboardBuffer::kStandard: |
| return true; |
| case mojom::ClipboardBuffer::kSelection: |
| return is_selection_buffer_available_; |
| } |
| return true; |
| } |
| |
| } // namespace blink |