blob: b3569d476bdc3914bbc27c86e8a65028bf852108 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/base/clipboard/clipboard_ozone.h"
#include <algorithm>
#include <limits>
#include <utility>
#include <vector>
#include "base/check.h"
#include "base/containers/contains.h"
#include "base/containers/flat_map.h"
#include "base/containers/map_util.h"
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/timer/timer.h"
#include "base/types/optional_util.h"
#include "base/types/variant_util.h"
#include "build/build_config.h"
#include "clipboard_util.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/clipboard_buffer.h"
#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/base/clipboard/clipboard_metrics.h"
#include "ui/base/clipboard/clipboard_monitor.h"
#include "ui/base/clipboard/clipboard_util.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
#include "ui/base/data_transfer_policy/data_transfer_endpoint_serializer.h"
#include "ui/base/data_transfer_policy/data_transfer_policy_controller.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/ozone/public/ozone_platform.h"
#include "ui/ozone/public/platform_clipboard.h"
namespace ui {
namespace {
// The amount of time to wait for a request to complete before aborting it.
constexpr base::TimeDelta kRequestTimeout = base::Seconds(1);
// Checks if DLP rules allow the clipboard read.
bool IsReadAllowed(absl::optional<DataTransferEndpoint> data_src,
const DataTransferEndpoint* data_dst,
const base::span<uint8_t> data) {
DataTransferPolicyController* policy_controller =
DataTransferPolicyController::Get();
if (!policy_controller || !data_src.has_value() || data.empty()) {
return true;
}
bool is_allowed = policy_controller->IsClipboardReadAllowed(
data_src, data_dst, data.size());
return is_allowed;
}
// Depending on the backend, the platform clipboard may or may not be
// available. Should it be absent, we provide a dummy one. It always calls
// back immediately with empty data. It starts without ownership of any buffers
// but will take and keep ownership after a call to OfferClipboardData(). By
// taking ownership, we allow ClipboardOzone to return existing data in
// ReadClipboardDataAndWait().
class StubPlatformClipboard : public PlatformClipboard {
public:
StubPlatformClipboard() = default;
~StubPlatformClipboard() override = default;
// PlatformClipboard:
void OfferClipboardData(
ClipboardBuffer buffer,
const PlatformClipboard::DataMap& data_map,
PlatformClipboard::OfferDataClosure callback) override {
is_owner_[buffer] = true;
std::move(callback).Run();
}
void RequestClipboardData(
ClipboardBuffer buffer,
const std::string& mime_type,
PlatformClipboard::RequestDataClosure callback) override {
std::move(callback).Run({});
}
void GetAvailableMimeTypes(
ClipboardBuffer buffer,
PlatformClipboard::GetMimeTypesClosure callback) override {
std::move(callback).Run({});
}
bool IsSelectionOwner(ClipboardBuffer buffer) override {
return is_owner_[buffer];
}
void SetClipboardDataChangedCallback(
PlatformClipboard::ClipboardDataChangedCallback cb) override {}
bool IsSelectionBufferAvailable() const override { return false; }
private:
base::flat_map<ClipboardBuffer, bool> is_owner_;
};
} // namespace
// A helper class that uses a request pattern to asynchronously communicate
// with the ozone::PlatformClipboard and fetch clipboard data with the
// specified MIME types.
class ClipboardOzone::AsyncClipboardOzone {
public:
explicit AsyncClipboardOzone(PlatformClipboard* platform_clipboard,
ClipboardOzone* clipboard_ozone)
: platform_clipboard_(platform_clipboard),
clipboard_ozone_(clipboard_ozone),
weak_factory_(this) {
DCHECK(platform_clipboard_);
// Set a callback to listen to requests to increase the clipboard sequence
// number.
auto update_sequence_cb =
base::BindRepeating(&AsyncClipboardOzone::OnClipboardDataChanged,
weak_factory_.GetWeakPtr());
platform_clipboard_->SetClipboardDataChangedCallback(
std::move(update_sequence_cb));
}
AsyncClipboardOzone(const AsyncClipboardOzone&) = delete;
AsyncClipboardOzone& operator=(const AsyncClipboardOzone&) = delete;
~AsyncClipboardOzone() = default;
bool IsSelectionBufferAvailable() const {
return platform_clipboard_->IsSelectionBufferAvailable();
}
base::span<uint8_t> ReadClipboardDataSetSourceAndWait(
ClipboardBuffer buffer,
const std::string& mime_type) {
const auto data = ReadClipboardDataAndWait(buffer, mime_type);
#if BUILDFLAG(IS_CHROMEOS_LACROS)
if (!data.empty()) {
const auto data_src =
ReadClipboardDataAndWait(buffer, kMimeTypeDataTransferEndpoint);
std::string data_src_json = std::string(
reinterpret_cast<char*>(data_src.data()), data_src.size());
std::unique_ptr<DataTransferEndpoint> dte =
ui::ConvertJsonToDataTransferEndpoint(data_src_json);
if (dte)
clipboard_ozone_->SetSource(buffer, std::move(dte));
}
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
return data;
}
std::vector<std::string> RequestMimeTypes(ClipboardBuffer buffer) {
if (buffer == ClipboardBuffer::kSelection && !IsSelectionBufferAvailable())
return {};
// We can use a fastpath if we are the owner of the selection.
if (platform_clipboard_->IsSelectionOwner(buffer)) {
std::vector<std::string> mime_types;
for (const auto& item : offered_data_[buffer])
mime_types.push_back(item.first);
return mime_types;
}
return GetMimeTypes(buffer);
}
void PrepareForWriting() { data_to_offer_.clear(); }
void OfferData(ClipboardBuffer buffer) {
if (buffer == ClipboardBuffer::kSelection && !IsSelectionBufferAvailable())
return;
Offer(buffer, std::move(data_to_offer_));
}
void Clear(ClipboardBuffer buffer) {
if (buffer == ClipboardBuffer::kSelection && !IsSelectionBufferAvailable())
return;
data_to_offer_.clear();
OfferData(buffer);
}
void InsertData(std::vector<uint8_t> data,
const std::set<std::string>& mime_types) {
auto wrapped_data = scoped_refptr<base::RefCountedBytes>(
base::RefCountedBytes::TakeVector(&data));
for (const auto& mime_type : mime_types) {
DCHECK_EQ(data_to_offer_.count(mime_type), 0U);
data_to_offer_[mime_type] = wrapped_data;
}
}
const ClipboardSequenceNumberToken& GetSequenceNumber(
ClipboardBuffer buffer) const {
return buffer == ClipboardBuffer::kCopyPaste ? clipboard_sequence_number_
: selection_sequence_number_;
}
private:
base::span<uint8_t> ReadClipboardDataAndWait(ClipboardBuffer buffer,
const std::string& mime_type) {
if (buffer == ClipboardBuffer::kSelection && !IsSelectionBufferAvailable())
return {};
// We can use a fastpath if we are the owner of the selection.
if (platform_clipboard_->IsSelectionOwner(buffer)) {
auto it = offered_data_[buffer].find(mime_type);
if (it == offered_data_[buffer].end())
return {};
return base::make_span(it->second->front(), it->second->size());
}
if (auto data = Read(buffer, mime_type))
return base::make_span(data->front(), data->size());
return {};
}
// Request<Result> encapsulates a clipboard request and provides a sync-like
// way to perform it, whereas Result is the operation return type. Request
// instances are created by factory functions (e.g: Read, Offer, GetMimeTypes,
// etc), which are supposed to know how to bind them to actual calls to the
// underlying platform clipboard instance. Factory functions usually create
// them as local vars (ie: stack memory), and bind them to weak references,
// through GetWeakPtr(), plumbing them into Finish* functions, which prevents
// use-after-free issues in case, for example, a platform clipboard callback
// would run with an already destroyed request instance.
template <typename Result>
class Request {
public:
enum class State { kStarted, kDone, kAborted };
// Blocks until the request is done or aborted. The |result_| is returned if
// the request succeeds, otherwise an empty value is returned.
Result TakeResultSync() {
// For a variety of reasons, it might already be done at this point,
// depending on the platform clipboard implementation and the specific
// request (e.g: cached values, sync request, etc).
if (state_ == State::kDone)
return std::move(result_);
DCHECK_EQ(state_, State::kStarted);
// TODO(crbug.com/913422): this is known to be dangerous, and may cause
// blocks in ui thread. But ui::Clipboard was designed with synchronous
// APIs rather than asynchronous ones, which platform clipboards can
// provide. E.g: X11 and Wayland.
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
quit_closure_ = run_loop.QuitClosure();
// Set a timeout timer after which the request will be aborted.
base::OneShotTimer abort_timer;
abort_timer.Start(FROM_HERE, kRequestTimeout,
base::BindOnce(&Request::Abort, GetWeakPtr()));
run_loop.Run();
return std::move(result_);
}
void Finish(const Result& result) {
DCHECK_EQ(state_, State::kStarted);
state_ = State::kDone;
result_ = result;
if (!quit_closure_.is_null())
std::move(quit_closure_).Run();
}
base::WeakPtr<Request<Result>> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
private:
void Abort() {
DCHECK_EQ(state_, State::kStarted);
Finish(Result{});
state_ = State::kAborted;
}
// Keeps track of the request state.
State state_ = State::kStarted;
// Holds the sync loop quit closure.
base::OnceClosure quit_closure_;
// Stores the request result.
Result result_ = {};
base::WeakPtrFactory<Request<Result>> weak_factory_{this};
};
std::vector<std::string> GetMimeTypes(ClipboardBuffer buffer) {
using MimeTypesRequest = Request<std::vector<std::string>>;
MimeTypesRequest request;
platform_clipboard_->GetAvailableMimeTypes(
buffer,
base::BindOnce(&MimeTypesRequest::Finish, request.GetWeakPtr()));
return request.TakeResultSync();
}
PlatformClipboard::Data Read(ClipboardBuffer buffer,
const std::string& mime_type) {
using ReadRequest = Request<PlatformClipboard::Data>;
ReadRequest request;
platform_clipboard_->RequestClipboardData(
buffer, mime_type,
base::BindOnce(&ReadRequest::Finish, request.GetWeakPtr()));
return request.TakeResultSync().release();
}
void Offer(ClipboardBuffer buffer, PlatformClipboard::DataMap data_map) {
using OfferRequest = Request<bool>;
OfferRequest request;
offered_data_[buffer] = data_map;
platform_clipboard_->OfferClipboardData(
buffer, data_map,
base::BindOnce(&OfferRequest::Finish, request.GetWeakPtr(), true));
request.TakeResultSync();
}
void OnClipboardDataChanged(ClipboardBuffer buffer) {
DCHECK(buffer == ClipboardBuffer::kCopyPaste ||
platform_clipboard_->IsSelectionBufferAvailable());
if (buffer == ClipboardBuffer::kCopyPaste)
clipboard_sequence_number_ = ClipboardSequenceNumberToken();
else
selection_sequence_number_ = ClipboardSequenceNumberToken();
ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged();
}
bool IsBufferSupported(ClipboardBuffer buffer) const {
return buffer == ClipboardBuffer::kCopyPaste ||
platform_clipboard_->IsSelectionBufferAvailable();
}
// Clipboard data accumulated for writing.
PlatformClipboard::DataMap data_to_offer_;
// Clipboard data that had been offered most recently. Used as a cache to
// read data if we still own it.
base::flat_map<ClipboardBuffer, PlatformClipboard::DataMap> offered_data_;
// Provides communication to a system clipboard under ozone level.
const raw_ptr<PlatformClipboard> platform_clipboard_ = nullptr;
// Reference to the ClipboardOzone object instantiating this
// ClipboardOzone::AsyncClipboardOzone object. It is used to set
// the correct source when some text is copied from Ash and pasted to Lacros.
const raw_ptr<ClipboardOzone> clipboard_ozone_;
ClipboardSequenceNumberToken clipboard_sequence_number_;
ClipboardSequenceNumberToken selection_sequence_number_;
base::WeakPtrFactory<AsyncClipboardOzone> weak_factory_;
};
// ClipboardOzone implementation.
ClipboardOzone::ClipboardOzone() {
auto* platform_clipboard =
OzonePlatform::GetInstance()->GetPlatformClipboard();
if (platform_clipboard) {
async_clipboard_ozone_ =
std::make_unique<ClipboardOzone::AsyncClipboardOzone>(
platform_clipboard, this);
} else {
static base::NoDestructor<StubPlatformClipboard> stub_platform_clipboard;
async_clipboard_ozone_ =
std::make_unique<ClipboardOzone::AsyncClipboardOzone>(
stub_platform_clipboard.get(), this);
}
}
ClipboardOzone::~ClipboardOzone() = default;
void ClipboardOzone::OnPreShutdown() {}
absl::optional<DataTransferEndpoint> ClipboardOzone::GetSource(
ClipboardBuffer buffer) const {
return base::OptionalFromPtr(base::FindPtrOrNull(data_src_, buffer));
}
const ClipboardSequenceNumberToken& ClipboardOzone::GetSequenceNumber(
ClipboardBuffer buffer) const {
return async_clipboard_ozone_->GetSequenceNumber(buffer);
}
bool ClipboardOzone::IsFormatAvailable(
const ClipboardFormatType& format,
ClipboardBuffer buffer,
const DataTransferEndpoint* data_dst) const {
DCHECK(CalledOnValidThread());
if (!IsReadAllowed(GetSource(buffer), data_dst, base::span<uint8_t>()))
return false;
auto available_types = async_clipboard_ozone_->RequestMimeTypes(buffer);
return base::Contains(available_types, format.GetName());
}
void ClipboardOzone::Clear(ClipboardBuffer buffer) {
async_clipboard_ozone_->Clear(buffer);
data_src_[buffer].reset();
}
std::vector<std::u16string> ClipboardOzone::GetStandardFormats(
ClipboardBuffer buffer,
const DataTransferEndpoint* data_dst) const {
std::vector<std::u16string> types;
auto available_types = async_clipboard_ozone_->RequestMimeTypes(buffer);
for (const auto& mime_type : available_types) {
if (mime_type == ClipboardFormatType::HtmlType().GetName() ||
mime_type == ClipboardFormatType::SvgType().GetName() ||
mime_type == ClipboardFormatType::RtfType().GetName() ||
mime_type == ClipboardFormatType::BitmapType().GetName() ||
mime_type == ClipboardFormatType::FilenamesType().GetName()) {
types.push_back(base::UTF8ToUTF16(mime_type));
continue;
}
// `WriteText` uses the following mime types for text, so if those types are
// available, we add kMimeTypeText to the list.
if ((mime_type == ClipboardFormatType::PlainTextType().GetName() ||
mime_type == kMimeTypeLinuxText || mime_type == kMimeTypeLinuxString ||
mime_type == kMimeTypeTextUtf8 ||
mime_type == kMimeTypeLinuxUtf8String) &&
!base::Contains(types, base::UTF8ToUTF16(kMimeTypeText))) {
types.push_back(base::UTF8ToUTF16(kMimeTypeText));
continue;
}
}
return types;
}
void ClipboardOzone::ReadAvailableTypes(
ClipboardBuffer buffer,
const DataTransferEndpoint* data_dst,
std::vector<std::u16string>* types) const {
DCHECK(CalledOnValidThread());
DCHECK(types);
if (!IsReadAllowed(GetSource(buffer), data_dst, base::span<uint8_t>()))
return;
types->clear();
for (const auto& mime_type : GetStandardFormats(buffer, data_dst)) {
types->push_back(mime_type);
}
// Special handling for chromium/x-web-custom-data.
// We must read the data and deserialize it to find the list
// of mime types to report.
if (IsFormatAvailable(ClipboardFormatType::WebCustomDataType(), buffer,
data_dst)) {
auto data = async_clipboard_ozone_->ReadClipboardDataSetSourceAndWait(
buffer, ClipboardFormatType::WebCustomDataType().GetName());
ReadCustomDataTypes(data, types);
}
}
void ClipboardOzone::ReadText(ClipboardBuffer buffer,
const DataTransferEndpoint* data_dst,
std::u16string* result) const {
DCHECK(CalledOnValidThread());
auto clipboard_data =
async_clipboard_ozone_->ReadClipboardDataSetSourceAndWait(buffer,
kMimeTypeText);
if (!IsReadAllowed(GetSource(buffer), data_dst, clipboard_data))
return;
RecordRead(ClipboardFormatMetric::kText);
*result = base::UTF8ToUTF16(base::StringPiece(
reinterpret_cast<char*>(clipboard_data.data()), clipboard_data.size()));
}
void ClipboardOzone::ReadAsciiText(ClipboardBuffer buffer,
const DataTransferEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
auto clipboard_data =
async_clipboard_ozone_->ReadClipboardDataSetSourceAndWait(buffer,
kMimeTypeText);
if (!IsReadAllowed(GetSource(buffer), data_dst, clipboard_data))
return;
RecordRead(ClipboardFormatMetric::kText);
result->assign(clipboard_data.begin(), clipboard_data.end());
}
void ClipboardOzone::ReadHTML(ClipboardBuffer buffer,
const DataTransferEndpoint* data_dst,
std::u16string* markup,
std::string* src_url,
uint32_t* fragment_start,
uint32_t* fragment_end) const {
DCHECK(CalledOnValidThread());
auto clipboard_data =
async_clipboard_ozone_->ReadClipboardDataSetSourceAndWait(buffer,
kMimeTypeHTML);
if (!IsReadAllowed(GetSource(buffer), data_dst, clipboard_data))
return;
RecordRead(ClipboardFormatMetric::kHtml);
markup->clear();
if (src_url)
src_url->clear();
*fragment_start = 0;
*fragment_end = 0;
*markup = base::UTF8ToUTF16(base::StringPiece(
reinterpret_cast<char*>(clipboard_data.data()), clipboard_data.size()));
DCHECK_LE(markup->length(), std::numeric_limits<uint32_t>::max());
*fragment_end = static_cast<uint32_t>(markup->length());
}
void ClipboardOzone::ReadSvg(ClipboardBuffer buffer,
const DataTransferEndpoint* data_dst,
std::u16string* result) const {
DCHECK(CalledOnValidThread());
auto clipboard_data =
async_clipboard_ozone_->ReadClipboardDataSetSourceAndWait(buffer,
kMimeTypeSvg);
if (!IsReadAllowed(GetSource(buffer), data_dst, clipboard_data))
return;
RecordRead(ClipboardFormatMetric::kSvg);
*result = base::UTF8ToUTF16(base::StringPiece(
reinterpret_cast<char*>(clipboard_data.data()), clipboard_data.size()));
}
void ClipboardOzone::ReadRTF(ClipboardBuffer buffer,
const DataTransferEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
auto clipboard_data =
async_clipboard_ozone_->ReadClipboardDataSetSourceAndWait(buffer,
kMimeTypeRTF);
if (!IsReadAllowed(GetSource(buffer), data_dst, clipboard_data))
return;
RecordRead(ClipboardFormatMetric::kRtf);
result->assign(clipboard_data.begin(), clipboard_data.end());
}
void ClipboardOzone::ReadPng(ClipboardBuffer buffer,
const DataTransferEndpoint* data_dst,
ReadPngCallback callback) const {
auto clipboard_data =
async_clipboard_ozone_->ReadClipboardDataSetSourceAndWait(buffer,
kMimeTypePNG);
if (!IsReadAllowed(GetSource(buffer), data_dst, clipboard_data)) {
std::move(callback).Run(std::vector<uint8_t>());
return;
}
RecordRead(ClipboardFormatMetric::kPng);
std::vector<uint8_t> png_data =
std::vector<uint8_t>(clipboard_data.begin(), clipboard_data.end());
std::move(callback).Run(png_data);
}
void ClipboardOzone::ReadCustomData(ClipboardBuffer buffer,
const std::u16string& type,
const DataTransferEndpoint* data_dst,
std::u16string* result) const {
DCHECK(CalledOnValidThread());
auto custom_data = async_clipboard_ozone_->ReadClipboardDataSetSourceAndWait(
buffer, kMimeTypeWebCustomData);
if (!IsReadAllowed(GetSource(buffer), data_dst, custom_data))
return;
RecordRead(ClipboardFormatMetric::kCustomData);
if (absl::optional<std::u16string> maybe_data =
ReadCustomDataForType(custom_data, type);
maybe_data) {
*result = std::move(*maybe_data);
}
}
void ClipboardOzone::ReadFilenames(ClipboardBuffer buffer,
const DataTransferEndpoint* data_dst,
std::vector<ui::FileInfo>* result) const {
DCHECK(CalledOnValidThread());
auto clipboard_data =
async_clipboard_ozone_->ReadClipboardDataSetSourceAndWait(
buffer, kMimeTypeURIList);
if (!IsReadAllowed(GetSource(buffer), data_dst, clipboard_data))
return;
RecordRead(ClipboardFormatMetric::kFilenames);
std::string uri_list(clipboard_data.begin(), clipboard_data.end());
*result = ui::URIListToFileInfos(uri_list);
}
void ClipboardOzone::ReadBookmark(const DataTransferEndpoint* data_dst,
std::u16string* title,
std::string* url) const {
DCHECK(CalledOnValidThread());
// TODO(msisov): This was left NOTIMPLEMENTED() in all the Linux platforms.
// |data_dst| should be supported for DLP when ReadBookmark() is implemented.
NOTIMPLEMENTED();
}
void ClipboardOzone::ReadData(const ClipboardFormatType& format,
const DataTransferEndpoint* data_dst,
std::string* result) const {
DCHECK(CalledOnValidThread());
auto clipboard_data =
async_clipboard_ozone_->ReadClipboardDataSetSourceAndWait(
ClipboardBuffer::kCopyPaste, format.GetName());
if (!IsReadAllowed(GetSource(ClipboardBuffer::kCopyPaste), data_dst,
clipboard_data))
return;
RecordRead(ClipboardFormatMetric::kData);
result->assign(clipboard_data.begin(), clipboard_data.end());
}
bool ClipboardOzone::IsSelectionBufferAvailable() const {
return async_clipboard_ozone_->IsSelectionBufferAvailable();
}
void ClipboardOzone::WritePortableTextRepresentation(ClipboardBuffer buffer,
const ObjectMap& objects) {
// Just like Non-Backed/X11 implementation does, copy text data from the
// copy/paste selection to the primary selection.
if (buffer != ClipboardBuffer::kCopyPaste || !IsSelectionBufferAvailable()) {
return;
}
auto text_iter = objects.find(base::VariantIndexOfType<Data, TextData>());
if (text_iter == objects.end()) {
return;
}
const auto& text_data = absl::get<TextData>(text_iter->second.data);
if (text_data.data.empty()) {
return;
}
async_clipboard_ozone_->PrepareForWriting();
WriteText(text_data.data);
async_clipboard_ozone_->OfferData(ClipboardBuffer::kSelection);
}
void ClipboardOzone::WritePortableAndPlatformRepresentations(
ClipboardBuffer buffer,
const ObjectMap& objects,
std::vector<Clipboard::PlatformRepresentation> platform_representations,
std::unique_ptr<DataTransferEndpoint> data_src) {
DCHECK(CalledOnValidThread());
async_clipboard_ozone_->PrepareForWriting();
DispatchPlatformRepresentations(std::move(platform_representations));
data_src_[buffer] = std::move(data_src);
#if BUILDFLAG(IS_CHROMEOS_LACROS)
AddClipboardSourceToDataOffer(buffer);
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
for (const auto& object : objects)
DispatchPortableRepresentation(object.second);
async_clipboard_ozone_->OfferData(buffer);
WritePortableTextRepresentation(buffer, objects);
}
void ClipboardOzone::WriteText(base::StringPiece text) {
std::vector<uint8_t> data(text.begin(), text.end());
async_clipboard_ozone_->InsertData(
std::move(data), {kMimeTypeText, kMimeTypeLinuxText, kMimeTypeLinuxString,
kMimeTypeTextUtf8, kMimeTypeLinuxUtf8String});
}
void ClipboardOzone::WriteHTML(
base::StringPiece markup,
absl::optional<base::StringPiece> /* source_url */,
ClipboardContentType /* content_type */) {
std::vector<uint8_t> data(markup.begin(), markup.end());
async_clipboard_ozone_->InsertData(std::move(data), {kMimeTypeHTML});
}
void ClipboardOzone::WriteSvg(base::StringPiece markup) {
std::vector<uint8_t> data(markup.begin(), markup.end());
async_clipboard_ozone_->InsertData(std::move(data), {kMimeTypeSvg});
}
void ClipboardOzone::WriteRTF(base::StringPiece rtf) {
std::vector<uint8_t> data(rtf.begin(), rtf.end());
async_clipboard_ozone_->InsertData(std::move(data), {kMimeTypeRTF});
}
void ClipboardOzone::WriteFilenames(std::vector<ui::FileInfo> filenames) {
std::string uri_list = ui::FileInfosToURIList(filenames);
std::vector<uint8_t> data(uri_list.begin(), uri_list.end());
async_clipboard_ozone_->InsertData(std::move(data), {kMimeTypeURIList});
}
void ClipboardOzone::WriteBookmark(base::StringPiece title,
base::StringPiece url) {
// Writes a Mozilla url (UTF16: URL, newline, title)
std::u16string bookmark =
base::StrCat({base::UTF8ToUTF16(url) + u"\n" + base::UTF8ToUTF16(title)});
std::vector<uint8_t> data(
reinterpret_cast<const uint8_t*>(bookmark.data()),
reinterpret_cast<const uint8_t*>(bookmark.data() + bookmark.size()));
async_clipboard_ozone_->InsertData(std::move(data), {kMimeTypeMozillaURL});
}
void ClipboardOzone::WriteWebSmartPaste() {
async_clipboard_ozone_->InsertData(std::vector<uint8_t>(),
{kMimeTypeWebkitSmartPaste});
}
void ClipboardOzone::WriteBitmap(const SkBitmap& bitmap) {
// Encode the bitmap to a PNG from the UI thread. Unfortunately we can't hop
// to a background thread to perform the encoding because clipboard writes are
// (unfortunately) currently synchronous. We could consider making writes
// async, then encode the image on a background sequence. We could also
// consider storing the image as a bitmap and only encoding to a PNG on paste
// (e.g. see https://crrev.com/c/3260985).
std::vector<uint8_t> png_bytes =
clipboard_util::EncodeBitmapToPngAcceptJank(bitmap);
if (!png_bytes.empty()) {
async_clipboard_ozone_->InsertData(std::move(png_bytes), {kMimeTypePNG});
}
}
void ClipboardOzone::WriteData(const ClipboardFormatType& format,
base::span<const uint8_t> data) {
std::vector<uint8_t> owned_data(data.begin(), data.end());
async_clipboard_ozone_->InsertData(std::move(owned_data), {format.GetName()});
}
#if BUILDFLAG(IS_CHROMEOS_LACROS)
void ClipboardOzone::AddClipboardSourceToDataOffer(
const ClipboardBuffer buffer) {
absl::optional<DataTransferEndpoint> data_src = GetSource(buffer);
if (!data_src)
return;
std::string dte_json = ConvertDataTransferEndpointToJson(*data_src);
const char* dte_json_c_string = dte_json.c_str();
std::vector<uint8_t> data(dte_json_c_string,
dte_json_c_string + dte_json.size());
async_clipboard_ozone_->InsertData(std::move(data),
{kMimeTypeDataTransferEndpoint});
}
void ClipboardOzone::SetSource(ClipboardBuffer buffer,
std::unique_ptr<DataTransferEndpoint> data_src) {
data_src_[buffer] = std::move(data_src);
}
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
} // namespace ui