| // 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_aura.h" |
| |
| #include <list> |
| |
| #include "base/basictypes.h" |
| #include "base/files/file_path.h" |
| #include "base/logging.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/stl_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/base/clipboard/custom_data_helper.h" |
| #include "ui/gfx/geometry/size.h" |
| |
| namespace ui { |
| |
| namespace { |
| const char kMimeTypeFilename[] = "chromium/filename"; |
| const char kMimeTypeBitmap[] = "image/bmp"; |
| const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data"; |
| const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste"; |
| const size_t kMaxClipboardSize = 1; |
| |
| // Clipboard data format used by AuraClipboard. |
| enum AuraClipboardFormat { |
| TEXT = 1 << 0, |
| HTML = 1 << 1, |
| RTF = 1 << 2, |
| BOOKMARK = 1 << 3, |
| BITMAP = 1 << 4, |
| CUSTOM = 1 << 5, |
| WEB = 1 << 6, |
| }; |
| |
| // ClipboardData contains data copied to the Clipboard for a variety of formats. |
| // It mostly just provides APIs to cleanly access and manipulate this data. |
| class ClipboardData { |
| public: |
| ClipboardData() |
| : web_smart_paste_(false), |
| format_(0) {} |
| |
| virtual ~ClipboardData() {} |
| |
| // Bitmask of AuraClipboardFormat types. |
| int format() const { return format_; } |
| |
| const std::string& text() const { return text_; } |
| void set_text(const std::string& text) { |
| text_ = text; |
| format_ |= TEXT; |
| } |
| |
| const std::string& markup_data() const { return markup_data_; } |
| void set_markup_data(const std::string& markup_data) { |
| markup_data_ = markup_data; |
| format_ |= HTML; |
| } |
| |
| const std::string& rtf_data() const { return rtf_data_; } |
| void SetRTFData(const std::string& rtf_data) { |
| rtf_data_ = rtf_data; |
| format_ |= RTF; |
| } |
| |
| const std::string& url() const { return url_; } |
| void set_url(const std::string& url) { |
| url_ = url; |
| format_ |= HTML; |
| } |
| |
| const std::string& bookmark_title() const { return bookmark_title_; } |
| void set_bookmark_title(const std::string& bookmark_title) { |
| bookmark_title_ = bookmark_title; |
| format_ |= BOOKMARK; |
| } |
| |
| const std::string& bookmark_url() const { return bookmark_url_; } |
| void set_bookmark_url(const std::string& bookmark_url) { |
| bookmark_url_ = bookmark_url; |
| format_ |= BOOKMARK; |
| } |
| |
| const SkBitmap& bitmap() const { return bitmap_; } |
| void SetBitmapData(const SkBitmap& bitmap) { |
| bitmap.copyTo(&bitmap_); |
| format_ |= BITMAP; |
| } |
| |
| const std::string& custom_data_format() const { return custom_data_format_; } |
| const std::string& custom_data_data() const { return custom_data_data_; } |
| void SetCustomData(const std::string& data_format, |
| const std::string& data_data) { |
| if (data_data.size() == 0) { |
| custom_data_data_.clear(); |
| custom_data_format_.clear(); |
| return; |
| } |
| custom_data_data_ = data_data; |
| custom_data_format_ = data_format; |
| format_ |= CUSTOM; |
| } |
| |
| bool web_smart_paste() const { return web_smart_paste_; } |
| void set_web_smart_paste(bool web_smart_paste) { |
| web_smart_paste_ = web_smart_paste; |
| format_ |= WEB; |
| } |
| |
| private: |
| // Plain text in UTF8 format. |
| std::string text_; |
| |
| // HTML markup data in UTF8 format. |
| std::string markup_data_; |
| std::string url_; |
| |
| // RTF data. |
| std::string rtf_data_; |
| |
| // Bookmark title in UTF8 format. |
| std::string bookmark_title_; |
| std::string bookmark_url_; |
| |
| // Filenames. |
| std::vector<std::string> files_; |
| |
| // Bitmap images. |
| SkBitmap bitmap_; |
| |
| // Data with custom format. |
| std::string custom_data_format_; |
| std::string custom_data_data_; |
| |
| // WebKit smart paste data. |
| bool web_smart_paste_; |
| |
| int format_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ClipboardData); |
| }; |
| |
| // Platform clipboard implementation for Aura. This handles things like format |
| // conversion, versioning of clipboard items etc. The goal is to roughly provide |
| // a substitute to platform clipboards on other platforms such as GtkClipboard |
| // on gtk or winapi clipboard on win. |
| class AuraClipboard { |
| public: |
| AuraClipboard() : sequence_number_(0) { |
| } |
| |
| ~AuraClipboard() { |
| Clear(); |
| } |
| |
| void Clear() { |
| sequence_number_++; |
| STLDeleteContainerPointers(data_list_.begin(), data_list_.end()); |
| data_list_.clear(); |
| } |
| |
| uint64_t sequence_number() const { |
| return sequence_number_; |
| } |
| |
| // Returns the data currently on the top of the clipboard stack, NULL if the |
| // clipboard stack is empty. |
| const ClipboardData* GetData() const { |
| if (data_list_.empty()) |
| return NULL; |
| return data_list_.front(); |
| } |
| |
| // 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(AuraClipboardFormat format) const { |
| switch (format) { |
| case TEXT: |
| return HasFormat(TEXT) || HasFormat(BOOKMARK); |
| 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(TEXT)) |
| *result = data->text(); |
| else if (HasFormat(HTML)) |
| *result = data->markup_data(); |
| else if (HasFormat(BOOKMARK)) |
| *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* fragment_start, |
| uint32* fragment_end) const { |
| markup->clear(); |
| if (src_url) |
| src_url->clear(); |
| *fragment_start = 0; |
| *fragment_end = 0; |
| |
| if (!HasFormat(HTML)) |
| return; |
| |
| const ClipboardData* data = GetData(); |
| *markup = base::UTF8ToUTF16(data->markup_data()); |
| *src_url = data->url(); |
| |
| *fragment_start = 0; |
| DCHECK_LE(markup->length(), kuint32max); |
| *fragment_end = static_cast<uint32>(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(RTF)) |
| return; |
| |
| *result = data->rtf_data(); |
| } |
| |
| // Reads image from the data at the top of clipboard stack. |
| SkBitmap ReadImage() const { |
| SkBitmap img; |
| if (!HasFormat(BITMAP)) |
| return img; |
| |
| // A shallow copy should be fine here, but just to be safe... |
| const SkBitmap& clipboard_bitmap = GetData()->bitmap(); |
| clipboard_bitmap.copyTo(&img); |
| 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(CUSTOM)) |
| return; |
| |
| ui::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 { |
| title->clear(); |
| url->clear(); |
| if (!HasFormat(BOOKMARK)) |
| return; |
| |
| const ClipboardData* data = GetData(); |
| *title = base::UTF8ToUTF16(data->bookmark_title()); |
| *url = data->bookmark_url(); |
| } |
| |
| void ReadData(const std::string& type, std::string* result) const { |
| result->clear(); |
| const ClipboardData* data = GetData(); |
| if (!HasFormat(CUSTOM) || type != data->custom_data_format()) |
| return; |
| |
| *result = data->custom_data_data(); |
| } |
| |
| // Writes |data| to the top of the clipboard stack. |
| void WriteData(ClipboardData* data) { |
| DCHECK(data); |
| AddToListEnsuringSize(data); |
| } |
| |
| private: |
| // True if the data on top of the clipboard stack has format |format|. |
| bool HasFormat(AuraClipboardFormat format) const { |
| const ClipboardData* data = GetData(); |
| if (!data) |
| return false; |
| |
| return data->format() & format; |
| } |
| |
| void AddToListEnsuringSize(ClipboardData* data) { |
| DCHECK(data); |
| sequence_number_++; |
| data_list_.push_front(data); |
| |
| // If the size of list becomes more than the maximum allowed, we delete the |
| // last element. |
| if (data_list_.size() > kMaxClipboardSize) { |
| ClipboardData* last = data_list_.back(); |
| data_list_.pop_back(); |
| delete last; |
| } |
| } |
| |
| // Stack containing various versions of ClipboardData. |
| std::list<ClipboardData*> data_list_; |
| |
| // Sequence number uniquely identifying clipboard state. |
| uint64_t sequence_number_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AuraClipboard); |
| }; |
| |
| AuraClipboard* aura_clipboard = NULL; |
| |
| AuraClipboard* GetClipboard() { |
| if (!aura_clipboard) |
| aura_clipboard = new AuraClipboard(); |
| return aura_clipboard; |
| } |
| |
| void DeleteClipboard() { |
| if (aura_clipboard) |
| delete aura_clipboard; |
| aura_clipboard = NULL; |
| } |
| |
| // Helper class to build a ClipboardData object and write it to clipboard. |
| class ClipboardDataBuilder { |
| public: |
| static void CommitToClipboard() { |
| GetClipboard()->WriteData(GetCurrentData()); |
| current_data_ = NULL; |
| } |
| |
| 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 ClipboardData* current_data_; |
| }; |
| |
| ClipboardData* ClipboardDataBuilder::current_data_ = NULL; |
| |
| } // namespace |
| |
| // Clipboard::FormatType implementation. |
| Clipboard::FormatType::FormatType() { |
| } |
| |
| Clipboard::FormatType::FormatType(const std::string& native_format) |
| : data_(native_format) { |
| } |
| |
| Clipboard::FormatType::~FormatType() { |
| } |
| |
| std::string Clipboard::FormatType::Serialize() const { |
| return data_; |
| } |
| |
| // static |
| Clipboard::FormatType Clipboard::FormatType::Deserialize( |
| const std::string& serialization) { |
| return FormatType(serialization); |
| } |
| |
| bool Clipboard::FormatType::operator<(const FormatType& other) const { |
| return data_ < other.data_; |
| } |
| |
| bool Clipboard::FormatType::Equals(const FormatType& other) const { |
| return data_ == other.data_; |
| } |
| |
| // Various predefined FormatTypes. |
| // static |
| Clipboard::FormatType Clipboard::GetFormatType( |
| const std::string& format_string) { |
| return FormatType::Deserialize(format_string); |
| } |
| |
| // static |
| const Clipboard::FormatType& Clipboard::GetUrlFormatType() { |
| CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeURIList)); |
| return type; |
| } |
| |
| // static |
| const Clipboard::FormatType& Clipboard::GetUrlWFormatType() { |
| return GetUrlFormatType(); |
| } |
| |
| // static |
| const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { |
| CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeText)); |
| return type; |
| } |
| |
| // static |
| const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { |
| return GetPlainTextFormatType(); |
| } |
| |
| // static |
| const Clipboard::FormatType& Clipboard::GetFilenameFormatType() { |
| CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeFilename)); |
| return type; |
| } |
| |
| // static |
| const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() { |
| return Clipboard::GetFilenameFormatType(); |
| } |
| |
| // static |
| const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { |
| CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeHTML)); |
| return type; |
| } |
| |
| // static |
| const Clipboard::FormatType& Clipboard::GetRtfFormatType() { |
| CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeRTF)); |
| return type; |
| } |
| |
| // static |
| const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { |
| CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeBitmap)); |
| return type; |
| } |
| |
| // static |
| const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { |
| CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebkitSmartPaste)); |
| return type; |
| } |
| |
| // static |
| const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { |
| CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); |
| return type; |
| } |
| |
| // static |
| const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() { |
| CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData)); |
| return type; |
| } |
| |
| // Clipboard factory method. |
| Clipboard* Clipboard::Create() { |
| return new ClipboardAura; |
| } |
| |
| // ClipboardAura implementation. |
| ClipboardAura::ClipboardAura() { |
| DCHECK(CalledOnValidThread()); |
| // Make sure clipboard is created. |
| GetClipboard(); |
| } |
| |
| ClipboardAura::~ClipboardAura() { |
| DCHECK(CalledOnValidThread()); |
| DeleteClipboard(); |
| } |
| |
| uint64 ClipboardAura::GetSequenceNumber(ClipboardType type) const { |
| DCHECK(CalledOnValidThread()); |
| return GetClipboard()->sequence_number(); |
| } |
| |
| bool ClipboardAura::IsFormatAvailable(const FormatType& format, |
| ClipboardType type) const { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(IsSupportedClipboardType(type)); |
| AuraClipboard* clipboard = GetClipboard(); |
| if (GetPlainTextFormatType().Equals(format) || |
| GetUrlFormatType().Equals(format)) |
| return clipboard->IsFormatAvailable(TEXT); |
| else if (GetHtmlFormatType().Equals(format)) |
| return clipboard->IsFormatAvailable(HTML); |
| else if (GetRtfFormatType().Equals(format)) |
| return clipboard->IsFormatAvailable(RTF); |
| else if (GetBitmapFormatType().Equals(format)) |
| return clipboard->IsFormatAvailable(BITMAP); |
| else if (GetWebKitSmartPasteFormatType().Equals(format)) |
| return clipboard->IsFormatAvailable(WEB); |
| else { |
| const ClipboardData* data = clipboard->GetData(); |
| if (data && data->custom_data_format() == format.ToString()) |
| return true; |
| } |
| return false; |
| } |
| |
| void ClipboardAura::Clear(ClipboardType type) { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(IsSupportedClipboardType(type)); |
| AuraClipboard* clipboard = GetClipboard(); |
| clipboard->Clear(); |
| } |
| |
| void ClipboardAura::ReadAvailableTypes(ClipboardType type, |
| std::vector<base::string16>* types, |
| bool* contains_filenames) const { |
| DCHECK(CalledOnValidThread()); |
| if (!types || !contains_filenames) { |
| NOTREACHED(); |
| return; |
| } |
| |
| types->clear(); |
| *contains_filenames = false; |
| if (IsFormatAvailable(GetPlainTextFormatType(), type)) |
| types->push_back(base::UTF8ToUTF16(GetPlainTextFormatType().ToString())); |
| if (IsFormatAvailable(GetHtmlFormatType(), type)) |
| types->push_back(base::UTF8ToUTF16(GetHtmlFormatType().ToString())); |
| if (IsFormatAvailable(GetRtfFormatType(), type)) |
| types->push_back(base::UTF8ToUTF16(GetRtfFormatType().ToString())); |
| if (IsFormatAvailable(GetBitmapFormatType(), type)) |
| types->push_back(base::UTF8ToUTF16(kMimeTypePNG)); |
| |
| AuraClipboard* clipboard = GetClipboard(); |
| if (clipboard->IsFormatAvailable(CUSTOM) && clipboard->GetData()) { |
| ui::ReadCustomDataTypes(clipboard->GetData()->custom_data_data().c_str(), |
| clipboard->GetData()->custom_data_data().size(), types); |
| } |
| } |
| |
| void ClipboardAura::ReadText(ClipboardType type, base::string16* result) const { |
| DCHECK(CalledOnValidThread()); |
| GetClipboard()->ReadText(result); |
| } |
| |
| void ClipboardAura::ReadAsciiText(ClipboardType type, |
| std::string* result) const { |
| DCHECK(CalledOnValidThread()); |
| GetClipboard()->ReadAsciiText(result); |
| } |
| |
| void ClipboardAura::ReadHTML(ClipboardType type, |
| base::string16* markup, |
| std::string* src_url, |
| uint32* fragment_start, |
| uint32* fragment_end) const { |
| DCHECK(CalledOnValidThread()); |
| GetClipboard()->ReadHTML(markup, src_url, fragment_start, fragment_end); |
| } |
| |
| void ClipboardAura::ReadRTF(ClipboardType type, std::string* result) const { |
| DCHECK(CalledOnValidThread()); |
| GetClipboard()->ReadRTF(result); |
| } |
| |
| SkBitmap ClipboardAura::ReadImage(ClipboardType type) const { |
| DCHECK(CalledOnValidThread()); |
| return GetClipboard()->ReadImage(); |
| } |
| |
| void ClipboardAura::ReadCustomData(ClipboardType clipboard_type, |
| const base::string16& type, |
| base::string16* result) const { |
| DCHECK(CalledOnValidThread()); |
| GetClipboard()->ReadCustomData(type, result); |
| } |
| |
| void ClipboardAura::ReadBookmark(base::string16* title, |
| std::string* url) const { |
| DCHECK(CalledOnValidThread()); |
| GetClipboard()->ReadBookmark(title, url); |
| } |
| |
| void ClipboardAura::ReadData(const FormatType& format, |
| std::string* result) const { |
| DCHECK(CalledOnValidThread()); |
| GetClipboard()->ReadData(format.ToString(), result); |
| } |
| |
| void ClipboardAura::WriteObjects(ClipboardType type, const ObjectMap& objects) { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(IsSupportedClipboardType(type)); |
| for (ObjectMap::const_iterator iter = objects.begin(); iter != objects.end(); |
| ++iter) { |
| DispatchObject(static_cast<ObjectType>(iter->first), iter->second); |
| } |
| ClipboardDataBuilder::CommitToClipboard(); |
| } |
| |
| void ClipboardAura::WriteText(const char* text_data, size_t text_len) { |
| ClipboardDataBuilder::WriteText(text_data, text_len); |
| } |
| |
| void ClipboardAura::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 ClipboardAura::WriteRTF(const char* rtf_data, size_t data_len) { |
| ClipboardDataBuilder::WriteRTF(rtf_data, data_len); |
| } |
| |
| void ClipboardAura::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 ClipboardAura::WriteWebSmartPaste() { |
| ClipboardDataBuilder::WriteWebSmartPaste(); |
| } |
| |
| void ClipboardAura::WriteBitmap(const SkBitmap& bitmap) { |
| ClipboardDataBuilder::WriteBitmap(bitmap); |
| } |
| |
| void ClipboardAura::WriteData(const FormatType& format, |
| const char* data_data, |
| size_t data_len) { |
| ClipboardDataBuilder::WriteData(format.ToString(), data_data, data_len); |
| } |
| |
| } // namespace ui |