blob: e08aeff4fd088923700044c61bec8650aef65520 [file] [log] [blame]
// Copyright (c) 2012 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/clipboard_non_backed.h"
#include <stdint.h>
#include <limits>
#include <list>
#include <memory>
#include <utility>
#include "base/check_op.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/strings/utf_string_conversions.h"
#include "skia/ext/skia_utils_base.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/base/clipboard/clipboard_data.h"
#include "ui/base/clipboard/clipboard_data_endpoint.h"
#include "ui/base/clipboard/clipboard_format_type.h"
#include "ui/base/clipboard/clipboard_metrics.h"
#include "ui/base/clipboard/clipboard_monitor.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/gfx/geometry/size.h"
#include "ui/ozone/buildflags.h"
namespace ui {
namespace {
const size_t kMaxClipboardSize = 1;
} // namespace
// Simple, internal implementation of a clipboard, handling things like format
// conversion, versioning, etc.
class ClipboardInternal {
public:
ClipboardInternal() = default;
~ClipboardInternal() = default;
void Clear() {
sequence_number_++;
data_list_.clear();
ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged();
}
uint64_t sequence_number() const { return sequence_number_; }
// Returns the data currently on the top of the clipboard stack, nullptr if
// the clipboard stack is empty.
const ClipboardData* GetData() const {
if (data_list_.empty())
return nullptr;
return data_list_.front().get();
}
// Returns true if the data on top of the clipboard stack has format |format|
// or another format that can be converted to |format|.
bool IsFormatAvailable(ClipboardInternalFormat format) const {
switch (format) {
case ClipboardInternalFormat::kText:
return HasFormat(ClipboardInternalFormat::kText) ||
HasFormat(ClipboardInternalFormat::kBookmark);
default:
return HasFormat(format);
}
}
// Reads text from the data at the top of clipboard stack.
void ReadText(base::string16* result) const {
std::string utf8_result;
ReadAsciiText(&utf8_result);
*result = base::UTF8ToUTF16(utf8_result);
}
// Reads ASCII text from the data at the top of clipboard stack.
void ReadAsciiText(std::string* result) const {
result->clear();
const ClipboardData* data = GetData();
if (!data)
return;
if (HasFormat(ClipboardInternalFormat::kText))
*result = data->text();
else if (HasFormat(ClipboardInternalFormat::kHtml))
*result = data->markup_data();
else if (HasFormat(ClipboardInternalFormat::kBookmark))
*result = data->bookmark_url();
}
// Reads HTML from the data at the top of clipboard stack.
void ReadHTML(base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
uint32_t* fragment_end) const {
markup->clear();
if (src_url)
src_url->clear();
*fragment_start = 0;
*fragment_end = 0;
if (!HasFormat(ClipboardInternalFormat::kHtml))
return;
const ClipboardData* data = GetData();
*markup = base::UTF8ToUTF16(data->markup_data());
*src_url = data->url();
*fragment_start = 0;
DCHECK_LE(markup->length(), std::numeric_limits<uint32_t>::max());
*fragment_end = static_cast<uint32_t>(markup->length());
}
// Reads RTF from the data at the top of clipboard stack.
void ReadRTF(std::string* result) const {
result->clear();
const ClipboardData* data = GetData();
if (!HasFormat(ClipboardInternalFormat::kRtf))
return;
*result = data->rtf_data();
}
// Reads image from the data at the top of clipboard stack.
SkBitmap ReadImage() const {
SkBitmap img;
if (!HasFormat(ClipboardInternalFormat::kBitmap))
return img;
// A shallow copy should be fine here, but just to be safe...
const SkBitmap& clipboard_bitmap = GetData()->bitmap();
if (img.tryAllocPixels(clipboard_bitmap.info())) {
clipboard_bitmap.readPixels(img.info(), img.getPixels(), img.rowBytes(),
0, 0);
}
return img;
}
// Reads data of type |type| from the data at the top of clipboard stack.
void ReadCustomData(const base::string16& type,
base::string16* result) const {
result->clear();
const ClipboardData* data = GetData();
if (!HasFormat(ClipboardInternalFormat::kCustom))
return;
ReadCustomDataForType(data->custom_data_data().c_str(),
data->custom_data_data().size(), type, result);
}
// Reads bookmark from the data at the top of clipboard stack.
void ReadBookmark(base::string16* title, std::string* url) const {
if (title)
title->clear();
if (url)
url->clear();
if (!HasFormat(ClipboardInternalFormat::kBookmark))
return;
const ClipboardData* data = GetData();
if (title)
*title = base::UTF8ToUTF16(data->bookmark_title());
if (url)
*url = data->bookmark_url();
}
void ReadData(const std::string& type, std::string* result) const {
result->clear();
const ClipboardData* data = GetData();
if (!HasFormat(ClipboardInternalFormat::kCustom) ||
type != data->custom_data_format())
return;
*result = data->custom_data_data();
}
// Writes |data| to the top of the clipboard stack.
void WriteData(std::unique_ptr<ClipboardData> data) {
DCHECK(data);
AddToListEnsuringSize(std::move(data));
ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged();
}
private:
// True if the data on top of the clipboard stack has format |format|.
bool HasFormat(ClipboardInternalFormat format) const {
const ClipboardData* data = GetData();
return data ? data->format() & static_cast<int>(format) : false;
}
void AddToListEnsuringSize(std::unique_ptr<ClipboardData> data) {
DCHECK(data);
sequence_number_++;
data_list_.push_front(std::move(data));
// If the size of list becomes more than the maximum allowed, we delete the
// last element.
if (data_list_.size() > kMaxClipboardSize) {
data_list_.pop_back();
}
}
// Stack containing various versions of ClipboardData.
std::list<std::unique_ptr<ClipboardData>> data_list_;
// Sequence number uniquely identifying clipboard state.
uint64_t sequence_number_ = 0;
DISALLOW_COPY_AND_ASSIGN(ClipboardInternal);
};
// Helper class to build a ClipboardData object and write it to clipboard.
class ClipboardDataBuilder {
public:
// If |data_src| is nullptr, this means that the data source isn't
// confidential and the data can be pasted in any document.
static void CommitToClipboard(
ClipboardInternal* clipboard,
std::unique_ptr<ClipboardDataEndpoint> data_src) {
ClipboardData* data = GetCurrentData();
data->set_source(std::move(data_src));
clipboard->WriteData(TakeCurrentData());
}
static void WriteText(const char* text_data, size_t text_len) {
ClipboardData* data = GetCurrentData();
data->set_text(std::string(text_data, text_len));
}
static void WriteHTML(const char* markup_data,
size_t markup_len,
const char* url_data,
size_t url_len) {
ClipboardData* data = GetCurrentData();
data->set_markup_data(std::string(markup_data, markup_len));
data->set_url(std::string(url_data, url_len));
}
static void WriteRTF(const char* rtf_data, size_t rtf_len) {
ClipboardData* data = GetCurrentData();
data->SetRTFData(std::string(rtf_data, rtf_len));
}
static void WriteBookmark(const char* title_data,
size_t title_len,
const char* url_data,
size_t url_len) {
ClipboardData* data = GetCurrentData();
data->set_bookmark_title(std::string(title_data, title_len));
data->set_bookmark_url(std::string(url_data, url_len));
}
static void WriteWebSmartPaste() {
ClipboardData* data = GetCurrentData();
data->set_web_smart_paste(true);
}
static void WriteBitmap(const SkBitmap& bitmap) {
ClipboardData* data = GetCurrentData();
data->SetBitmapData(bitmap);
}
static void WriteData(const std::string& format,
const char* data_data,
size_t data_len) {
ClipboardData* data = GetCurrentData();
data->SetCustomData(format, std::string(data_data, data_len));
}
private:
static ClipboardData* GetCurrentData() {
if (!current_data_)
current_data_ = new ClipboardData;
return current_data_;
}
static std::unique_ptr<ClipboardData> TakeCurrentData() {
std::unique_ptr<ClipboardData> data = base::WrapUnique(GetCurrentData());
current_data_ = nullptr;
return data;
}
// This is a raw pointer instead of a std::unique_ptr to avoid adding a
// static initializer.
static ClipboardData* current_data_;
};
ClipboardData* ClipboardDataBuilder::current_data_ = nullptr;
// linux-chromeos uses non-backed clipboard by default, but supports ozone x11
// with flag --use-system-clipbboard.
#if !defined(OS_CHROMEOS) || !BUILDFLAG(OZONE_PLATFORM_X11)
// Clipboard factory method.
Clipboard* Clipboard::Create() {
return new ClipboardNonBacked;
}
#endif
// ClipboardNonBacked implementation.
ClipboardNonBacked::ClipboardNonBacked()
: clipboard_internal_(std::make_unique<ClipboardInternal>()) {
DCHECK(CalledOnValidThread());
}
ClipboardNonBacked::~ClipboardNonBacked() {
DCHECK(CalledOnValidThread());
}
void ClipboardNonBacked::OnPreShutdown() {}
uint64_t ClipboardNonBacked::GetSequenceNumber(ClipboardBuffer buffer) const {
DCHECK(CalledOnValidThread());
return clipboard_internal_->sequence_number();
}
bool ClipboardNonBacked::IsFormatAvailable(const ClipboardFormatType& format,
ClipboardBuffer buffer) const {
DCHECK(CalledOnValidThread());
DCHECK(IsSupportedClipboardBuffer(buffer));
if (ClipboardFormatType::GetPlainTextType().Equals(format) ||
ClipboardFormatType::GetUrlType().Equals(format))
return clipboard_internal_->IsFormatAvailable(
ClipboardInternalFormat::kText);
if (ClipboardFormatType::GetHtmlType().Equals(format))
return clipboard_internal_->IsFormatAvailable(
ClipboardInternalFormat::kHtml);
if (ClipboardFormatType::GetRtfType().Equals(format))
return clipboard_internal_->IsFormatAvailable(
ClipboardInternalFormat::kRtf);
if (ClipboardFormatType::GetBitmapType().Equals(format))
return clipboard_internal_->IsFormatAvailable(
ClipboardInternalFormat::kBitmap);
if (ClipboardFormatType::GetWebKitSmartPasteType().Equals(format))
return clipboard_internal_->IsFormatAvailable(
ClipboardInternalFormat::kWeb);
const ClipboardData* data = clipboard_internal_->GetData();
return data && data->custom_data_format() == format.GetName();
}
void ClipboardNonBacked::Clear(ClipboardBuffer buffer) {
DCHECK(CalledOnValidThread());
DCHECK(IsSupportedClipboardBuffer(buffer));
clipboard_internal_->Clear();
}
void ClipboardNonBacked::ReadAvailableTypes(
ClipboardBuffer buffer,
std::vector<base::string16>* types) const {
DCHECK(CalledOnValidThread());
DCHECK(types);
types->clear();
if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer))
types->push_back(
base::UTF8ToUTF16(ClipboardFormatType::GetPlainTextType().GetName()));
if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer))
types->push_back(
base::UTF8ToUTF16(ClipboardFormatType::GetHtmlType().GetName()));
if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer))
types->push_back(
base::UTF8ToUTF16(ClipboardFormatType::GetRtfType().GetName()));
if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer))
types->push_back(base::UTF8ToUTF16(kMimeTypePNG));
if (clipboard_internal_->IsFormatAvailable(
ClipboardInternalFormat::kCustom) &&
clipboard_internal_->GetData()) {
ReadCustomDataTypes(
clipboard_internal_->GetData()->custom_data_data().c_str(),
clipboard_internal_->GetData()->custom_data_data().size(), types);
}
}
std::vector<base::string16>
ClipboardNonBacked::ReadAvailablePlatformSpecificFormatNames(
ClipboardBuffer buffer) const {
DCHECK(CalledOnValidThread());
std::vector<base::string16> types;
// Includes all non-pickled AvailableTypes.
if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer)) {
types.push_back(
base::UTF8ToUTF16(ClipboardFormatType::GetPlainTextType().GetName()));
}
if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer)) {
types.push_back(
base::UTF8ToUTF16(ClipboardFormatType::GetHtmlType().GetName()));
}
if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer)) {
types.push_back(
base::UTF8ToUTF16(ClipboardFormatType::GetRtfType().GetName()));
}
if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer)) {
types.push_back(base::UTF8ToUTF16(kMimeTypePNG));
}
return types;
}
void ClipboardNonBacked::ReadText(ClipboardBuffer buffer,
base::string16* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kText);
clipboard_internal_->ReadText(result);
}
void ClipboardNonBacked::ReadAsciiText(ClipboardBuffer buffer,
std::string* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kText);
clipboard_internal_->ReadAsciiText(result);
}
void ClipboardNonBacked::ReadHTML(ClipboardBuffer buffer,
base::string16* markup,
std::string* src_url,
uint32_t* fragment_start,
uint32_t* fragment_end) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kHtml);
clipboard_internal_->ReadHTML(markup, src_url, fragment_start, fragment_end);
}
void ClipboardNonBacked::ReadRTF(ClipboardBuffer buffer,
std::string* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kRtf);
clipboard_internal_->ReadRTF(result);
}
void ClipboardNonBacked::ReadImage(ClipboardBuffer buffer,
ReadImageCallback callback) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kImage);
std::move(callback).Run(clipboard_internal_->ReadImage());
}
void ClipboardNonBacked::ReadCustomData(ClipboardBuffer buffer,
const base::string16& type,
base::string16* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kCustomData);
clipboard_internal_->ReadCustomData(type, result);
}
void ClipboardNonBacked::ReadBookmark(base::string16* title,
std::string* url) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kBookmark);
clipboard_internal_->ReadBookmark(title, url);
}
void ClipboardNonBacked::ReadData(const ClipboardFormatType& format,
std::string* result) const {
DCHECK(CalledOnValidThread());
RecordRead(ClipboardFormatMetric::kData);
clipboard_internal_->ReadData(format.GetName(), result);
}
void ClipboardNonBacked::WritePortableRepresentations(
ClipboardBuffer buffer,
const ObjectMap& objects,
std::unique_ptr<ClipboardDataEndpoint> data_src) {
DCHECK(CalledOnValidThread());
DCHECK(IsSupportedClipboardBuffer(buffer));
for (const auto& object : objects)
DispatchPortableRepresentation(object.first, object.second);
ClipboardDataBuilder::CommitToClipboard(clipboard_internal_.get(),
std::move(data_src));
}
void ClipboardNonBacked::WritePlatformRepresentations(
ClipboardBuffer buffer,
std::vector<Clipboard::PlatformRepresentation> platform_representations,
std::unique_ptr<ClipboardDataEndpoint> data_src) {
DCHECK(CalledOnValidThread());
DCHECK(IsSupportedClipboardBuffer(buffer));
DispatchPlatformRepresentations(std::move(platform_representations));
ClipboardDataBuilder::CommitToClipboard(clipboard_internal_.get(),
std::move(data_src));
}
void ClipboardNonBacked::WriteText(const char* text_data, size_t text_len) {
ClipboardDataBuilder::WriteText(text_data, text_len);
}
void ClipboardNonBacked::WriteHTML(const char* markup_data,
size_t markup_len,
const char* url_data,
size_t url_len) {
ClipboardDataBuilder::WriteHTML(markup_data, markup_len, url_data, url_len);
}
void ClipboardNonBacked::WriteRTF(const char* rtf_data, size_t data_len) {
ClipboardDataBuilder::WriteRTF(rtf_data, data_len);
}
void ClipboardNonBacked::WriteBookmark(const char* title_data,
size_t title_len,
const char* url_data,
size_t url_len) {
ClipboardDataBuilder::WriteBookmark(title_data, title_len, url_data, url_len);
}
void ClipboardNonBacked::WriteWebSmartPaste() {
ClipboardDataBuilder::WriteWebSmartPaste();
}
void ClipboardNonBacked::WriteBitmap(const SkBitmap& bitmap) {
ClipboardDataBuilder::WriteBitmap(bitmap);
}
void ClipboardNonBacked::WriteData(const ClipboardFormatType& format,
const char* data_data,
size_t data_len) {
ClipboardDataBuilder::WriteData(format.GetName(), data_data, data_len);
}
} // namespace ui