Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(11)

Unified Diff: ui/base/clipboard/clipboard_aurax11.cc

Issue 10829341: Desktop aura: Use the X11 clipboard (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix MessageLoop ordering in tests. Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/base/clipboard/clipboard.h ('k') | ui/base/clipboard/clipboard_chromeos.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/base/clipboard/clipboard_aurax11.cc
diff --git a/ui/base/clipboard/clipboard_aurax11.cc b/ui/base/clipboard/clipboard_aurax11.cc
index 0a2c4913c7f07f7b13910ecd5595ccf840ba1668..b37d636a9bae80627881eced330babda3560c039 100644
--- a/ui/base/clipboard/clipboard_aurax11.cc
+++ b/ui/base/clipboard/clipboard_aurax11.cc
@@ -4,488 +4,912 @@
#include "ui/base/clipboard/clipboard.h"
+#include <X11/extensions/Xfixes.h>
+#include <X11/Xatom.h>
#include <list>
+#include <set>
#include "base/basictypes.h"
#include "base/file_path.h"
+#include "base/i18n/icu_string_conversions.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/message_pump_aurax11.h"
+#include "base/message_pump_observer.h"
+#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/utf_string_conversions.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/custom_data_helper.h"
+#include "ui/base/x/x11_atom_cache.h"
+#include "ui/base/x/x11_util.h"
+
#include "ui/gfx/size.h"
namespace ui {
namespace {
-const char kMimeTypeFilename[] = "chromium/filename";
+
+const char kChromeSelection[] = "CHROME_SELECTION";
+const char kClipboard[] = "CLIPBOARD";
const char kMimeTypeBitmap[] = "image/bmp";
+const char kMimeTypeFilename[] = "chromium/filename";
+const char kMimeTypeMozillaURL[] = "text/x-moz-url";
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,
+const char kMultiple[] = "MULTIPLE";
+const char kString[] = "STRING";
+const char kTargets[] = "TARGETS";
+const char kText[] = "TEXT";
+const char kUtf8String[] = "UTF8_STRING";
+
+const char* kAtomsToCache[] = {
+ kChromeSelection,
+ kClipboard,
+ kMimeTypeBitmap,
+ kMimeTypeFilename,
+ kMimeTypeMozillaURL,
+ kMimeTypeWebkitSmartPaste,
+ kMultiple,
+ kString,
+ kTargets,
+ kText,
+ kUtf8String,
+ NULL
};
-// 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()
- : bitmap_data_(),
- web_smart_paste_(false),
- format_(0) {}
+///////////////////////////////////////////////////////////////////////////////
- virtual ~ClipboardData() {}
+// Returns a list of all text atoms that we handle.
+std::vector< ::Atom> GetTextAtomsFrom(const X11AtomCache* atom_cache) {
+ std::vector< ::Atom> atoms;
+ atoms.push_back(atom_cache->GetAtom(kUtf8String));
+ atoms.push_back(atom_cache->GetAtom(kString));
+ atoms.push_back(atom_cache->GetAtom(kText));
+ return atoms;
+}
- // Bitmask of AuraClipboardFormat types.
- const int format() const { return format_; }
+///////////////////////////////////////////////////////////////////////////////
- const std::string& text() const { return text_; }
- void set_text(const std::string& text) {
- text_ = text;
- format_ |= TEXT;
- }
+// Uses the XFixes API to provide sequence numbers for GetSequenceNumber().
+class SelectionChangeObserver : public base::MessagePumpObserver {
+ public:
+ static SelectionChangeObserver* GetInstance();
- const std::string& markup_data() const { return markup_data_; }
- void set_markup_data(const std::string& markup_data) {
- markup_data_ = markup_data;
- format_ |= HTML;
+ uint64 clipboard_sequence_number() const {
+ return clipboard_sequence_number_;
}
+ uint64 primary_sequence_number() const { return primary_sequence_number_; }
- const std::string& rtf_data() const { return rtf_data_; }
- void SetRTFData(const std::string& rtf_data) {
- rtf_data_ = rtf_data;
- format_ |= RTF;
- }
+ private:
+ friend struct DefaultSingletonTraits<SelectionChangeObserver>;
- const std::string& url() const { return url_; }
- void set_url(const std::string& url) {
- url_ = url;
- format_ |= HTML;
- }
+ SelectionChangeObserver();
+ ~SelectionChangeObserver();
- const std::string& bookmark_title() const { return bookmark_title_; }
- void set_bookmark_title(const std::string& bookmark_title) {
- bookmark_title_ = bookmark_title;
- format_ |= BOOKMARK;
- }
+ // Overridden from base::MessagePumpObserver:
+ virtual base::EventStatus WillProcessEvent(
+ const base::NativeEvent& event) OVERRIDE;
+ virtual void DidProcessEvent(
+ const base::NativeEvent& event) OVERRIDE {}
- const std::string& bookmark_url() const { return bookmark_url_; }
- void set_bookmark_url(const std::string& bookmark_url) {
- bookmark_url_ = bookmark_url;
- format_ |= BOOKMARK;
- }
+ int event_base_;
+ Atom clipboard_atom_;
+ uint64 clipboard_sequence_number_;
+ uint64 primary_sequence_number_;
- uint8_t* bitmap_data() const { return bitmap_data_.get(); }
- const gfx::Size& bitmap_size() const { return bitmap_size_; }
- void SetBitmapData(const char* pixel_data, const char* size_data) {
- bitmap_size_ = *reinterpret_cast<const gfx::Size*>(size_data);
+ DISALLOW_COPY_AND_ASSIGN(SelectionChangeObserver);
+};
- // We assume 4-byte pixel data.
- size_t bitmap_data_len = 4 * bitmap_size_.width() * bitmap_size_.height();
- bitmap_data_.reset(new uint8_t[bitmap_data_len]);
- memcpy(bitmap_data_.get(), pixel_data, bitmap_data_len);
- format_ |= BITMAP;
+SelectionChangeObserver::SelectionChangeObserver()
+ : event_base_(-1),
+ clipboard_atom_(None),
+ clipboard_sequence_number_(0),
+ primary_sequence_number_(0) {
+ int ignored;
+ if (XFixesQueryExtension(GetXDisplay(), &event_base_, &ignored)) {
+ clipboard_atom_ = XInternAtom(GetXDisplay(), kClipboard, false);
+ XFixesSelectSelectionInput(GetXDisplay(), GetX11RootWindow(),
+ clipboard_atom_,
+ XFixesSetSelectionOwnerNotifyMask |
+ XFixesSelectionWindowDestroyNotifyMask |
+ XFixesSelectionClientCloseNotifyMask);
+ // This seems to be semi-optional. For some reason, registering for any
+ // selection notify events seems to subscribe us to events for both the
+ // primary and the clipboard buffers. Register anyway just to be safe.
+ XFixesSelectSelectionInput(GetXDisplay(), GetX11RootWindow(),
+ XA_PRIMARY,
+ XFixesSetSelectionOwnerNotifyMask |
+ XFixesSelectionWindowDestroyNotifyMask |
+ XFixesSelectionClientCloseNotifyMask);
+
+ base::MessagePumpAuraX11::Current()->AddObserver(this);
}
+}
+
+SelectionChangeObserver::~SelectionChangeObserver() {
+ // We are a singleton; we will outlive our message pump.
+}
+
+SelectionChangeObserver* SelectionChangeObserver::GetInstance() {
+ return Singleton<SelectionChangeObserver>::get();
+}
- 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;
+base::EventStatus SelectionChangeObserver::WillProcessEvent(
+ const base::NativeEvent& event) {
+ if (event->type == event_base_ + XFixesSelectionNotify) {
+ XFixesSelectionNotifyEvent* ev =
+ reinterpret_cast<XFixesSelectionNotifyEvent*>(event);
+ if (ev->selection == clipboard_atom_) {
+ clipboard_sequence_number_++;
+ } else if (ev->selection == XA_PRIMARY) {
+ primary_sequence_number_++;
+ } else {
+ DLOG(ERROR) << "Unexpected selection atom: " << ev->selection;
}
- custom_data_data_ = data_data;
- custom_data_format_ = data_format;
- format_ |= CUSTOM;
}
+ return base::EVENT_CONTINUE;
+}
- 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;
- }
+///////////////////////////////////////////////////////////////////////////////
+
+// Represents the selection in different data formats. Binary data passed in is
+// assumed to be allocated with new char[], and is owned by FormatMap.
+class FormatMap {
+ public:
+ // Our internal data store, which we only expose through iterators.
+ typedef std::map< ::Atom, std::pair<char*, size_t> > InternalMap;
+ typedef std::map< ::Atom, std::pair<char*, size_t> >::const_iterator
+ const_iterator;
+
+ FormatMap();
+ ~FormatMap();
+
+ // Adds the selection in the format |atom|. Ownership of |data| is passed to
+ // us.
+ void Insert(::Atom atom, char* data, size_t size);
+
+ // Pass through to STL map. Only allow non-mutation access.
+ const_iterator begin() const { return data_.begin(); }
+ const_iterator end() const { return data_.end(); }
+ const_iterator find(::Atom atom) const { return data_.find(atom); }
+ size_t size() const { return data_.size(); }
private:
- // Plain text in UTF8 format.
- std::string text_;
+ InternalMap data_;
+
+ DISALLOW_COPY_AND_ASSIGN(FormatMap);
+};
- // HTML markup data in UTF8 format.
- std::string markup_data_;
- std::string url_;
+FormatMap::FormatMap() {}
- // RTF data.
- std::string rtf_data_;
+FormatMap::~FormatMap() {
+ // WriteText() inserts the same pointer multiple times for different
+ // representations; we need to dedupe it.
+ std::set<char*> to_delete;
+ for (InternalMap::iterator it = data_.begin(); it != data_.end(); ++it)
+ to_delete.insert(it->second.first);
- // Bookmark title in UTF8 format.
- std::string bookmark_title_;
- std::string bookmark_url_;
+ for (std::set<char*>::iterator it = to_delete.begin(); it != to_delete.end();
+ ++it) {
+ delete [] *it;
+ }
+}
- // Filenames.
- std::vector<std::string> files_;
+void FormatMap::Insert(::Atom atom, char* data, size_t size) {
+ DCHECK(data_.find(atom) == data_.end());
+ data_.insert(std::make_pair(atom, std::make_pair(data, size)));
+}
- // Bitmap images.
- scoped_array<uint8_t> bitmap_data_;
- gfx::Size bitmap_size_;
+///////////////////////////////////////////////////////////////////////////////
- // Data with custom format.
- std::string custom_data_format_;
- std::string custom_data_data_;
+// Represents a list of possible return types. Copy constructable.
+class TargetList {
+ public:
+ typedef std::vector< ::Atom> AtomVector;
- // WebKit smart paste data.
- bool web_smart_paste_;
+ TargetList(const AtomVector& target_list, X11AtomCache* atom_cache);
- int format_;
+ bool ContainsText() const;
+ bool ContainsFormat(const Clipboard::FormatType& format_type) const;
+ bool ContainsAtom(::Atom atom) const;
- DISALLOW_COPY_AND_ASSIGN(ClipboardData);
+ private:
+ AtomVector target_list_;
+ X11AtomCache* atom_cache_;
};
-// 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() {}
+TargetList::TargetList(const AtomVector& target_list,
+ X11AtomCache* atom_cache)
+ : target_list_(target_list),
+ atom_cache_(atom_cache) {
+}
- ~AuraClipboard() {
- Clear();
+bool TargetList::ContainsText() const {
+ std::vector< ::Atom> atoms = GetTextAtomsFrom(atom_cache_);
+ for (std::vector< ::Atom>::const_iterator it = atoms.begin();
+ it != atoms.end(); ++it) {
+ if (ContainsAtom(*it))
+ return true;
}
- void Clear() {
- STLDeleteContainerPointers(data_list_.begin(), data_list_.end());
- data_list_.clear();
- }
+ return false;
+}
- // Returns the number of entries currently in the clipboard stack.
- size_t GetNumClipboardEntries() {
- return data_list_.size();
- }
+bool TargetList::ContainsFormat(
+ const Clipboard::FormatType& format_type) const {
+ ::Atom atom = atom_cache_->GetAtom(format_type.ToString().c_str());
+ return ContainsAtom(atom);
+}
- // 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();
- }
+bool TargetList::ContainsAtom(::Atom atom) const {
+ return find(target_list_.begin(), target_list_.end(), atom)
+ != target_list_.end();
+}
- // 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(string16* result) const {
- std::string utf8_result;
- ReadAsciiText(&utf8_result);
- *result = UTF8ToUTF16(utf8_result);
- }
+// A holder for data with optional X11 deletion semantics.
+class SelectionData {
+ public:
+ // |atom_cache| is still owned by caller.
+ explicit SelectionData(X11AtomCache* atom_cache);
+ ~SelectionData();
- // 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();
- }
+ ::Atom type() const { return type_; }
+ char* data() const { return data_; }
+ size_t size() const { return size_; }
- // Reads HTML from the data at the top of clipboard stack.
- void ReadHTML(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 = UTF8ToUTF16(data->markup_data());
- *src_url = data->url();
-
- *fragment_start = 0;
- DCHECK_LE(markup->length(), kuint32max);
- *fragment_end = static_cast<uint32>(markup->length());
- }
+ void Set(::Atom type, char* data, size_t size, bool owned);
- // 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;
+ // If |type_| is a string type, convert the data to UTF8 and return it.
+ std::string GetText() const;
- *result = data->rtf_data();
- }
+ // Assigns the raw data to the string.
+ void AssignTo(std::string* result) const;
- // Reads image from the data at the top of clipboard stack.
- SkBitmap ReadImage() const {
- SkBitmap img;
- if (!HasFormat(BITMAP))
- return img;
-
- const ClipboardData* data = GetData();
- const gfx::Size size = data->bitmap_size();
- uint8_t* bitmap = data->bitmap_data();
- img.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height(), 0);
- img.allocPixels();
- img.eraseARGB(0, 0, 0, 0);
- memcpy(img.getPixels(), bitmap, size.width() * size.height() * 4);
- return img;
- }
+ private:
+ ::Atom type_;
+ char* data_;
+ size_t size_;
+ bool owned_;
- // Reads data of type |type| from the data at the top of clipboard stack.
- void ReadCustomData(const string16& type, string16* result) const {
- result->clear();
- const ClipboardData* data = GetData();
- if (!HasFormat(CUSTOM))
- return;
+ X11AtomCache* atom_cache_;
+};
- ui::ReadCustomDataForType(data->custom_data_data().c_str(),
- data->custom_data_data().size(),
- type, result);
- }
+SelectionData::SelectionData(X11AtomCache* atom_cache)
+ : type_(None),
+ data_(NULL),
+ size_(0),
+ owned_(false),
+ atom_cache_(atom_cache) {
+}
- // Reads bookmark from the data at the top of clipboard stack.
- void ReadBookmark(string16* title, std::string* url) const {
- title->clear();
- url->clear();
- if (!HasFormat(BOOKMARK))
- return;
+SelectionData::~SelectionData() {
+ if (owned_)
+ XFree(data_);
+}
- const ClipboardData* data = GetData();
- *title = UTF8ToUTF16(data->bookmark_title());
- *url = data->bookmark_url();
- }
+void SelectionData::Set(::Atom type, char* data, size_t size, bool owned) {
+ if (owned_)
+ XFree(data_);
- 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;
+ type_ = type;
+ data_ = data;
+ size_ = size;
+ owned_ = owned;
+}
- *result = data->custom_data_data();
+std::string SelectionData::GetText() const {
+ if (type_ == atom_cache_->GetAtom(kUtf8String) ||
+ type_ == atom_cache_->GetAtom(kText)) {
+ return std::string(data_, size_);
+ } else if (type_ == atom_cache_->GetAtom(kString)) {
+ std::string result;
+ base::ConvertToUtf8AndNormalize(std::string(data_, size_),
+ base::kCodepageLatin1,
+ &result);
+ return result;
+ } else {
+ // BTW, I looked at COMPOUND_TEXT, and there's no way we're going to
+ // support that. Yuck.
+ NOTREACHED();
+ return std::string();
}
+}
- // Writes |data| to the top of the clipboard stack.
- void WriteData(ClipboardData* data) {
- DCHECK(data);
- AddToListEnsuringSize(data);
- }
+void SelectionData::AssignTo(std::string* result) const {
+ result->assign(data_, size_);
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+
+// I would love for the FormatType to really be a wrapper around an X11 ::Atom,
+// but there are a few problems. Chromeos unit tests spawn a new X11 server for
+// each test, so Atom numeric values don't persist across tests. We could still
+// maybe deal with that if we didn't have static accessor methods everywhere.
+
+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::Equals(const FormatType& other) const {
+ return data_ == other.data_;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Clipboard::AuraX11Details
+
+// Private implementation of our X11 integration. Keeps X11 headers out of the
+// majority of chrome, which break badly.
+class Clipboard::AuraX11Details : public base::MessagePumpDispatcher {
+ public:
+ AuraX11Details();
+ ~AuraX11Details();
+
+ X11AtomCache* atom_cache() { return &atom_cache_; }
+
+ // Returns the X11 type that we pass to various XSelection functions for the
+ // given buffer.
+ ::Atom LookupSelectionForBuffer(Buffer buffer) const;
+
+ // Finds the FormatMap for the incoming selection atom.
+ FormatMap* LookupStorageForAtom(::Atom atom);
+
+ // As we need to collect all the data types before we tell X11 that we own a
+ // particular selection, we create a temporary clipboard mapping that
+ // InsertMapping writes to. Then we commit it in TakeOwnershipOfSelection,
+ // where we save it in one of the clipboard data slots.
+ void CreateNewClipboardData();
+
+ // Inserts a mapping into clipboard_data_.
+ void InsertMapping(const std::string& key, char* data, size_t data_len);
+
+ // Moves the temporary |clipboard_data_| to the long term data storage for
+ // |buffer|.
+ void TakeOwnershipOfSelection(Buffer buffer);
+
+ // Returns the first of |types| offered by the current selection holder in
+ // |data_out|, or returns NULL if none of those types are available.
+ //
+ // If the selection holder is us, this call is synchronous and we pull
+ // the data out of |clipboard_selection_| or |primary_selection_|. If the
+ // selection holder is some other window, we spin up a nested message loop
+ // and do the asynchronous dance with whatever application is holding the
+ // selection.
+ scoped_ptr<SelectionData> RequestAndWaitForTypes(
+ Buffer buffer,
+ const std::vector< ::Atom>& types);
+
+ // Retrieves the list of possible data types the current clipboard owner has.
+ //
+ // If the selection holder is us, this is synchronous, otherwise this runs a
+ // blocking message loop.
+ TargetList WaitAndGetTargetsList(Buffer buffer);
+
+ // Does the work of requesting |target| from |selection_name|, spinning up
+ // the nested message loop, and reading the resulting data back. |out_data|
+ // is allocated with the X allocator and must be freed with
+ // XFree(). |out_data_bytes| is the length in machine chars, while
+ // |out_data_items| is the length in |out_type| items.
+ bool PerformBlockingConvertSelection(::Atom selection_name,
+ ::Atom target,
+ unsigned char** out_data,
+ size_t* out_data_bytes,
+ size_t* out_data_items,
+ ::Atom* out_type);
+
+ // Returns a list of all text atoms that we handle.
+ std::vector< ::Atom> GetTextAtoms() const;
+
+ // Returns a vector with a |format| converted to an X11 atom.
+ std::vector< ::Atom> GetAtomsForFormat(const Clipboard::FormatType& format);
+
+ // Clears a certain data buffer.
+ void Clear(Buffer buffer);
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;
+ // Called by Dispatch to handle specific types of events.
+ void HandleSelectionRequest(const XSelectionRequestEvent& event);
+ void HandleSelectionNotify(const XSelectionEvent& event);
+ void HandleSelectionClear(const XSelectionClearEvent& event);
+ void HandlePropertyNotify(const XPropertyEvent& event);
- return data->format() & format;
- }
+ // Overridden from base::MessagePumpDispatcher:
+ virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
- void AddToListEnsuringSize(ClipboardData* data) {
- DCHECK(data);
- data_list_.push_front(data);
+ // Temporary target map that we write to during DispatchObects.
+ scoped_ptr<FormatMap> clipboard_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;
- }
- }
+ // The current value of our clipboard and primary selections. These should be
+ // non-NULL when we own the selection.
+ scoped_ptr<FormatMap> clipboard_selection_;
+ scoped_ptr<FormatMap> primary_selection_;
+
+ // Our X11 state.
+ Display* x_display_;
+ ::Window x_root_window_;
+
+ // Input-only window used as a selection owner.
+ ::Window x_window_;
+
+ // True if we're currently running a nested message loop, waiting for data to
+ // come back from the X server.
+ bool in_nested_loop_;
- // Stack containing various versions of ClipboardData.
- std::list<ClipboardData*> data_list_;
+ // Data to the current XConvertSelection request. Used for error detection;
+ // we verify it on the return message.
+ ::Atom current_selection_;
+ ::Atom current_target_;
- DISALLOW_COPY_AND_ASSIGN(AuraClipboard);
+ // The property in the returning SelectNotify message is used to signal
+ // success. If None, our request failed somehow. If equal to the property
+ // atom that we sent in the XConvertSelection call, we can read that property
+ // on |x_window_| for the requested data.
+ ::Atom returned_property_;
+
+ // Called to terminate the nested message loop.
+ base::Closure quit_closure_;
+
+ X11AtomCache atom_cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(AuraX11Details);
};
-AuraClipboard* aura_clipboard = NULL;
+Clipboard::AuraX11Details::AuraX11Details()
+ : x_display_(GetXDisplay()),
+ x_root_window_(DefaultRootWindow(x_display_)),
+ in_nested_loop_(false),
+ atom_cache_(x_display_, kAtomsToCache) {
+ // We don't know all possible MIME types at compile time.
+ atom_cache_.allow_uncached_atoms();
+
+ x_window_ = XCreateWindow(
+ x_display_, x_root_window_,
+ -100, -100, 10, 10, // x, y, width, height
+ 0, // border width
+ CopyFromParent, // depth
+ InputOnly,
+ CopyFromParent, // visual
+ 0,
+ NULL);
+ XStoreName(x_display_, x_window_, "Chromium clipboard");
+ XSelectInput(x_display_, x_window_, PropertyChangeMask);
+
+ base::MessagePumpAuraX11::Current()->AddDispatcherForWindow(this, x_window_);
+}
+
+Clipboard::AuraX11Details::~AuraX11Details() {
+ base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(x_window_);
-AuraClipboard* GetClipboard() {
- if (!aura_clipboard)
- aura_clipboard = new AuraClipboard();
- return aura_clipboard;
+ XDestroyWindow(x_display_, x_window_);
}
-void DeleteClipboard() {
- if (aura_clipboard)
- delete aura_clipboard;
- aura_clipboard = NULL;
+::Atom Clipboard::AuraX11Details::LookupSelectionForBuffer(
+ Buffer buffer) const {
+ if (buffer == BUFFER_STANDARD)
+ return atom_cache_.GetAtom(kClipboard);
+ else
+ return XA_PRIMARY;
}
-// Helper class to build a ClipboardData object and write it to clipboard.
-class ClipboardDataBuilder {
- public:
- static void CommitToClipboard() {
- DCHECK(current_data_);
- GetClipboard()->WriteData(current_data_);
- current_data_ = NULL;
- }
+FormatMap* Clipboard::AuraX11Details::LookupStorageForAtom(::Atom atom) {
+ if (atom == XA_PRIMARY)
+ return primary_selection_.get();
+ else if (atom == atom_cache_.GetAtom(kClipboard))
+ return clipboard_selection_.get();
+ else
+ return NULL;
+}
- static void WriteText(const char* text_data, size_t text_len) {
- ClipboardData* data = GetCurrentData();
- data->set_text(std::string(text_data, text_len));
- }
+void Clipboard::AuraX11Details::CreateNewClipboardData() {
+ clipboard_data_.reset(new FormatMap);
+}
- 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));
- }
+void Clipboard::AuraX11Details::InsertMapping(const std::string& key,
+ char* data,
+ size_t data_len) {
+ ::Atom atom_key = atom_cache_.GetAtom(key.c_str());
+ clipboard_data_->Insert(atom_key, data, data_len);
+}
- static void WriteRTF(const char* rtf_data, size_t rtf_len) {
- ClipboardData* data = GetCurrentData();
- data->SetRTFData(std::string(rtf_data, rtf_len));
+void Clipboard::AuraX11Details::TakeOwnershipOfSelection(Buffer buffer) {
+ // Tell the X server that we are now the selection owner.
+ ::Atom xselection = LookupSelectionForBuffer(buffer);
+ XSetSelectionOwner(x_display_, xselection, x_window_, CurrentTime);
+
+ if (XGetSelectionOwner(x_display_, xselection) == x_window_) {
+ // The X server agrees that we are the selection owner. Commit our data.
+ if (buffer == BUFFER_STANDARD)
+ clipboard_selection_ = clipboard_data_.Pass();
+ else
+ primary_selection_ = clipboard_data_.Pass();
}
+}
- 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));
+scoped_ptr<SelectionData> Clipboard::AuraX11Details::RequestAndWaitForTypes(
+ Buffer buffer,
+ const std::vector< ::Atom>& types) {
+ ::Atom selection_name = LookupSelectionForBuffer(buffer);
+ if (XGetSelectionOwner(x_display_, selection_name) == x_window_) {
+ // We can local fastpath instead of playing the nested message loop game
+ // with the X server.
+ FormatMap* format_map = LookupStorageForAtom(selection_name);
+ DCHECK(format_map);
+
+ for (std::vector< ::Atom>::const_iterator it = types.begin();
+ it != types.end(); ++it) {
+ FormatMap::const_iterator format_map_it = format_map->find(*it);
+ if (format_map_it != format_map->end()) {
+ scoped_ptr<SelectionData> data_out(new SelectionData(&atom_cache_));
+ data_out->Set(format_map_it->first, format_map_it->second.first,
+ format_map_it->second.second, false);
+ return data_out.Pass();
+ }
+ }
+ } else {
+ TargetList targets = WaitAndGetTargetsList(buffer);
+
+ for (std::vector< ::Atom>::const_iterator it = types.begin();
+ it != types.end(); ++it) {
+ unsigned char* data = NULL;
+ size_t data_bytes = 0;
+ ::Atom type = None;
+ if (targets.ContainsAtom(*it) &&
+ PerformBlockingConvertSelection(selection_name,
+ *it,
+ &data,
+ &data_bytes,
+ NULL,
+ &type) &&
+ type == *it) {
+ scoped_ptr<SelectionData> data_out(new SelectionData(&atom_cache_));
+ data_out->Set(type, (char*)data, data_bytes, true);
+ return data_out.Pass();
+ }
+ }
}
- static void WriteWebSmartPaste() {
- ClipboardData* data = GetCurrentData();
- data->set_web_smart_paste(true);
- }
+ return scoped_ptr<SelectionData>();
+}
- static void WriteBitmap(const char* pixel_data, const char* size_data) {
- ClipboardData* data = GetCurrentData();
- data->SetBitmapData(pixel_data, size_data);
+TargetList Clipboard::AuraX11Details::WaitAndGetTargetsList(
+ Buffer buffer) {
+ ::Atom selection_name = LookupSelectionForBuffer(buffer);
+ std::vector< ::Atom> out;
+ if (XGetSelectionOwner(x_display_, selection_name) == x_window_) {
+ // We can local fastpath and return the list of local targets.
+ FormatMap* format_map = LookupStorageForAtom(selection_name);
+ DCHECK(format_map);
+
+ for (FormatMap::const_iterator it = format_map->begin();
+ it != format_map->end(); ++it) {
+ out.push_back(it->first);
+ }
+ } else {
+ unsigned char* data = NULL;
+ size_t out_data_items = 0;
+ ::Atom out_type = None;
+
+ if (PerformBlockingConvertSelection(selection_name,
+ atom_cache_.GetAtom(kTargets),
+ &data,
+ NULL,
+ &out_data_items,
+ &out_type)) {
+ ::Atom* atom_array = reinterpret_cast< ::Atom*>(data);
+ for (size_t i = 0; i < out_data_items; ++i)
+ out.push_back(atom_array[i]);
+
+ XFree(data);
+ } else {
+ // There was no target list. Most Java apps doesn't offer a TARGETS list,
+ // even though they AWT to. They will offer individual text types if you
+ // ask. If this is the case we attempt to make sense of the contents as
+ // text. This is pretty unfortunate since it means we have to actually
+ // copy the data to see if it is available, but at least this path
+ // shouldn't be hit for conforming programs.
+ std::vector< ::Atom> types = GetTextAtoms();
+ for (std::vector< ::Atom>::const_iterator it = types.begin();
+ it != types.end(); ++it) {
+ ::Atom type = None;
+ if (PerformBlockingConvertSelection(selection_name,
+ *it,
+ NULL,
+ NULL,
+ NULL,
+ &type) &&
+ type == *it) {
+ out.push_back(*it);
+ }
+ }
+ }
}
- 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));
+ return TargetList(out, &atom_cache_);
+}
+
+bool Clipboard::AuraX11Details::PerformBlockingConvertSelection(
+ ::Atom selection_name,
+ ::Atom target,
+ unsigned char** out_data,
+ size_t* out_data_bytes,
+ size_t* out_data_items,
+ ::Atom* out_type) {
+ // The name of the property we're asking to be set on |x_window_|.
+ ::Atom property_to_set = atom_cache_.GetAtom(kChromeSelection);
+
+ XConvertSelection(x_display_,
+ selection_name,
+ target,
+ property_to_set,
+ x_window_,
+ CurrentTime);
+
+ // Now that we've thrown our message off to the X11 server, we block waiting
+ // for a response.
+ MessageLoopForUI* loop = MessageLoopForUI::current();
+ MessageLoop::ScopedNestableTaskAllower allow_nested(loop);
+ base::RunLoop run_loop(base::MessagePumpAuraX11::Current());
+
+ current_selection_ = selection_name;
+ current_target_ = target;
+ in_nested_loop_ = true;
+ quit_closure_ = run_loop.QuitClosure();
+ run_loop.Run();
+ in_nested_loop_ = false;
+ current_selection_ = None;
+ current_target_ = None;
+
+ if (returned_property_ != property_to_set)
+ return false;
+
+ // Retrieve the data from our window.
+ unsigned long nitems = 0;
+ unsigned long nbytes = 0;
+ Atom prop_type = None;
+ int prop_format = 0;
+ unsigned char* property_data = NULL;
+ if (XGetWindowProperty(x_display_,
+ x_window_,
+ returned_property_,
+ 0, 0x1FFFFFFF /* MAXINT32 / 4 */, False,
+ AnyPropertyType, &prop_type, &prop_format,
+ &nitems, &nbytes, &property_data) != Success) {
+ return false;
}
- private:
- static ClipboardData* GetCurrentData() {
- if (!current_data_)
- current_data_ = new ClipboardData;
- return current_data_;
+ if (prop_type == None)
+ return false;
+
+ if (out_data)
+ *out_data = property_data;
+
+ if (out_data_bytes) {
+ // So even though we should theoretically have nbytes (and we can't
+ // pass NULL there), we need to manually calculate the byte length here
+ // because nbytes always returns zero.
+ switch (prop_format) {
+ case 8:
+ *out_data_bytes = nitems;
+ break;
+ case 16:
+ *out_data_bytes = sizeof(short) * nitems;
+ break;
+ case 32:
+ *out_data_bytes = sizeof(long) * nitems;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
}
- static ClipboardData* current_data_;
-};
+ if (out_data_items)
+ *out_data_items = nitems;
-ClipboardData* ClipboardDataBuilder::current_data_ = NULL;
+ if (out_type)
+ *out_type = prop_type;
-} // namespace
+ return true;
+}
-Clipboard::FormatType::FormatType() {
+std::vector< ::Atom> Clipboard::AuraX11Details::GetTextAtoms() const {
+ return GetTextAtomsFrom(&atom_cache_);
}
-Clipboard::FormatType::FormatType(const std::string& native_format)
- : data_(native_format) {
+std::vector< ::Atom> Clipboard::AuraX11Details::GetAtomsForFormat(
+ const Clipboard::FormatType& format) {
+ std::vector< ::Atom> atoms;
+ atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str()));
+ return atoms;
}
-Clipboard::FormatType::~FormatType() {
+void Clipboard::AuraX11Details::Clear(Buffer buffer) {
+ ::Atom selection_name = LookupSelectionForBuffer(buffer);
+ if (XGetSelectionOwner(x_display_, selection_name) == x_window_)
+ XSetSelectionOwner(x_display_, selection_name, None, CurrentTime);
+
+ if (buffer == BUFFER_STANDARD)
+ clipboard_selection_.reset();
+ else
+ primary_selection_.reset();
}
-std::string Clipboard::FormatType::Serialize() const {
- return data_;
+void Clipboard::AuraX11Details::HandleSelectionRequest(
+ const XSelectionRequestEvent& event) {
+ // Incrementally build our selection. By default this is a refusal, and we'll
+ // override the parts indicating success in the different cases.
+ XEvent reply;
+ reply.xselection.type = SelectionNotify;
+ reply.xselection.requestor = event.requestor;
+ reply.xselection.selection = event.selection;
+ reply.xselection.target = event.target;
+ reply.xselection.property = None; // Indicates failure
+ reply.xselection.time = event.time;
+
+ // Get the proper selection.
+ FormatMap* format_map = LookupStorageForAtom(event.selection);
+ if (format_map) {
+ ::Atom targets_atom = atom_cache_.GetAtom(kTargets);
+ if (event.target == targets_atom) {
+ std::vector< ::Atom> targets;
+ targets.push_back(targets_atom);
+ for (FormatMap::const_iterator it = format_map->begin();
+ it != format_map->end(); ++it) {
+ targets.push_back(it->first);
+ }
+
+ XChangeProperty(x_display_, event.requestor, event.property, XA_ATOM, 32,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(&targets.front()),
+ targets.size());
+ reply.xselection.property = event.property;
+ } else if (event.target == atom_cache_.GetAtom(kMultiple)) {
+ // TODO(erg): Theoretically, the spec claims I'm supposed to handle the
+ // MULTIPLE case, but I haven't seen it in the wild yet.
+ NOTIMPLEMENTED();
+ } else {
+ // Try to find the data type in map.
+ FormatMap::const_iterator it = format_map->find(event.target);
+ if (it != format_map->end()) {
+ XChangeProperty(x_display_, event.requestor, event.property,
+ event.target, 8,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(it->second.first),
+ it->second.second);
+ reply.xselection.property = event.property;
+ }
+ // I would put error logging here, but GTK ignores TARGETS and spams us
+ // looking for its own internal types.
+ }
+ } else {
+ DLOG(ERROR) << "Requested on a selection we don't support: "
+ << XGetAtomName(x_display_, event.selection);
+ }
+
+ // Send off the reply.
+ XSendEvent(x_display_, event.requestor, False, 0, &reply);
}
-// static
-Clipboard::FormatType Clipboard::FormatType::Deserialize(
- const std::string& serialization) {
- return FormatType(serialization);
+void Clipboard::AuraX11Details::HandleSelectionNotify(
+ const XSelectionEvent& event) {
+ if (!in_nested_loop_) {
+ // This shouldn't happen; we're not waiting on the X server for data, but
+ // any client can send any message...
+ return;
+ }
+
+ if (current_selection_ == event.selection &&
+ current_target_ == event.target) {
+ returned_property_ = event.property;
+ } else {
+ // I am assuming that if some other client sent us a message after we've
+ // asked for data, but it's malformed, we should just treat as if they sent
+ // us an error message.
+ returned_property_ = None;
+ }
+
+ quit_closure_.Run();
}
-bool Clipboard::FormatType::Equals(const FormatType& other) const {
- return data_ == other.data_;
+void Clipboard::AuraX11Details::HandleSelectionClear(
+ const XSelectionClearEvent& event) {
+ DLOG(ERROR) << "SelectionClear";
+
+ // TODO(erg): If we receive a SelectionClear event while we're handling data,
+ // we need to delay clearing.
+}
+
+void Clipboard::AuraX11Details::HandlePropertyNotify(
+ const XPropertyEvent& event) {
+ // TODO(erg): Must handle PropertyNotify events on our |x_window_| as part
+ // of receiving data during paste.
+}
+
+bool Clipboard::AuraX11Details::Dispatch(const base::NativeEvent& event) {
+ XEvent* xev = event;
+
+ switch (xev->type) {
+ case SelectionRequest:
+ HandleSelectionRequest(xev->xselectionrequest);
+ break;
+ case SelectionNotify:
+ HandleSelectionNotify(xev->xselection);
+ break;
+ case SelectionClear:
+ HandleSelectionClear(xev->xselectionclear);
+ break;
+ case PropertyNotify:
+ HandlePropertyNotify(xev->xproperty);
+ break;
+ default:
+ break;
+ }
+
+ return true;
}
-Clipboard::Clipboard() {
+///////////////////////////////////////////////////////////////////////////////
+// Clipboard
+
+Clipboard::Clipboard()
+ : aurax11_details_(new AuraX11Details) {
DCHECK(CalledOnValidThread());
- // Make sure clipboard is created.
- GetClipboard();
}
Clipboard::~Clipboard() {
DCHECK(CalledOnValidThread());
- DeleteClipboard();
+
+ // TODO(erg): We need to do whatever the equivalent of
+ // gtk_clipboard_store(clipboard_) is here. When we shut down, we want the
+ // current selection to live on.
}
void Clipboard::WriteObjects(Buffer buffer, const ObjectMap& objects) {
DCHECK(CalledOnValidThread());
DCHECK(IsValidBuffer(buffer));
+
+ aurax11_details_->CreateNewClipboardData();
for (ObjectMap::const_iterator iter = objects.begin();
iter != objects.end(); ++iter) {
DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
}
- ClipboardDataBuilder::CommitToClipboard();
+
+ aurax11_details_->TakeOwnershipOfSelection(buffer);
}
bool Clipboard::IsFormatAvailable(const FormatType& format,
Buffer buffer) const {
DCHECK(CalledOnValidThread());
DCHECK(IsValidBuffer(buffer));
- 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;
+
+ TargetList target_list = aurax11_details_->WaitAndGetTargetsList(buffer);
+ return target_list.ContainsFormat(format);
}
void Clipboard::Clear(Buffer buffer) {
DCHECK(CalledOnValidThread());
DCHECK(IsValidBuffer(buffer));
- AuraClipboard* clipboard = GetClipboard();
- clipboard->Clear();
+ aurax11_details_->Clear(buffer);
}
void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types,
@@ -496,109 +920,222 @@ void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types,
return;
}
+ TargetList target_list = aurax11_details_->WaitAndGetTargetsList(buffer);
+
types->clear();
+
+ if (target_list.ContainsText())
+ types->push_back(UTF8ToUTF16(kMimeTypeText));
+ if (target_list.ContainsFormat(GetHtmlFormatType()))
+ types->push_back(UTF8ToUTF16(kMimeTypeHTML));
+ if (target_list.ContainsFormat(GetRtfFormatType()))
+ types->push_back(UTF8ToUTF16(kMimeTypeRTF));
+ if (target_list.ContainsFormat(GetBitmapFormatType()))
+ types->push_back(UTF8ToUTF16(kMimeTypePNG));
*contains_filenames = false;
- if (IsFormatAvailable(GetPlainTextFormatType(), buffer))
- types->push_back(UTF8ToUTF16(GetPlainTextFormatType().ToString()));
- if (IsFormatAvailable(GetHtmlFormatType(), buffer))
- types->push_back(UTF8ToUTF16(GetHtmlFormatType().ToString()));
- if (IsFormatAvailable(GetRtfFormatType(), buffer))
- types->push_back(UTF8ToUTF16(GetRtfFormatType().ToString()));
- if (IsFormatAvailable(GetBitmapFormatType(), buffer))
- types->push_back(UTF8ToUTF16(GetBitmapFormatType().ToString()));
-
- 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);
- }
+
+ scoped_ptr<SelectionData> data(aurax11_details_->RequestAndWaitForTypes(
+ buffer,
+ aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType())));
+ if (!data.get())
+ return;
+
+ ReadCustomDataTypes(data->data(), data->size(), types);
}
void Clipboard::ReadText(Buffer buffer, string16* result) const {
DCHECK(CalledOnValidThread());
- GetClipboard()->ReadText(result);
+
+ scoped_ptr<SelectionData> data(aurax11_details_->RequestAndWaitForTypes(
+ buffer, aurax11_details_->GetTextAtoms()));
+ if (data.get()) {
+ std::string text = data->GetText();
+ *result = UTF8ToUTF16(text);
+ }
}
void Clipboard::ReadAsciiText(Buffer buffer, std::string* result) const {
DCHECK(CalledOnValidThread());
- GetClipboard()->ReadAsciiText(result);
+
+ scoped_ptr<SelectionData> data(aurax11_details_->RequestAndWaitForTypes(
+ buffer, aurax11_details_->GetTextAtoms()));
+ if (data.get())
+ result->assign(data->GetText());
}
+// TODO(estade): handle different charsets.
+// TODO(port): set *src_url.
void Clipboard::ReadHTML(Buffer buffer,
string16* markup,
std::string* src_url,
uint32* fragment_start,
uint32* fragment_end) const {
DCHECK(CalledOnValidThread());
- GetClipboard()->ReadHTML(markup, src_url, fragment_start, fragment_end);
+ markup->clear();
+ if (src_url)
+ src_url->clear();
+ *fragment_start = 0;
+ *fragment_end = 0;
+
+ scoped_ptr<SelectionData> data(aurax11_details_->RequestAndWaitForTypes(
+ buffer, aurax11_details_->GetAtomsForFormat(GetHtmlFormatType())));
+ if (!data.get())
+ return;
+
+ // If the data starts with 0xFEFF, i.e., Byte Order Mark, assume it is
+ // UTF-16, otherwise assume UTF-8.
+ if (data->size() >= 2 &&
+ reinterpret_cast<const uint16_t*>(data->data())[0] == 0xFEFF) {
+ markup->assign(reinterpret_cast<const uint16_t*>(data->data()) + 1,
+ (data->size() / 2) - 1);
+ } else {
+ UTF8ToUTF16(reinterpret_cast<const char*>(data->data()), data->size(),
+ markup);
+ }
+
+ // If there is a terminating NULL, drop it.
+ if (!markup->empty() && markup->at(markup->length() - 1) == '\0')
+ markup->resize(markup->length() - 1);
+
+ *fragment_start = 0;
+ DCHECK(markup->length() <= kuint32max);
+ *fragment_end = static_cast<uint32>(markup->length());
}
void Clipboard::ReadRTF(Buffer buffer, std::string* result) const {
DCHECK(CalledOnValidThread());
- GetClipboard()->ReadRTF(result);
+
+ scoped_ptr<SelectionData> data(aurax11_details_->RequestAndWaitForTypes(
+ buffer, aurax11_details_->GetAtomsForFormat(GetRtfFormatType())));
+ if (data.get())
+ data->AssignTo(result);
}
SkBitmap Clipboard::ReadImage(Buffer buffer) const {
DCHECK(CalledOnValidThread());
- return GetClipboard()->ReadImage();
+ NOTIMPLEMENTED();
+ return SkBitmap();
}
void Clipboard::ReadCustomData(Buffer buffer,
const string16& type,
string16* result) const {
DCHECK(CalledOnValidThread());
- GetClipboard()->ReadCustomData(type, result);
+
+ scoped_ptr<SelectionData> data(aurax11_details_->RequestAndWaitForTypes(
+ buffer,
+ aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType())));
+ if (!data.get())
+ return;
+
+ ReadCustomDataForType(data->data(), data->size(), type, result);
}
void Clipboard::ReadBookmark(string16* title, std::string* url) const {
DCHECK(CalledOnValidThread());
- GetClipboard()->ReadBookmark(title, url);
+ // TODO(erg): This was left NOTIMPLEMENTED() in the gtk port too.
+ NOTIMPLEMENTED();
}
void Clipboard::ReadData(const FormatType& format, std::string* result) const {
DCHECK(CalledOnValidThread());
- GetClipboard()->ReadData(format.ToString(), result);
+
+ scoped_ptr<SelectionData> data(aurax11_details_->RequestAndWaitForTypes(
+ BUFFER_STANDARD, aurax11_details_->GetAtomsForFormat(format)));
+ if (data.get())
+ data->AssignTo(result);
+}
+
+// When a URL is copied from a render view context menu (via "copy link
+// location", for example), we additionally stick it in the X clipboard. This
+// matches other linux browsers.
+void Clipboard::DidWriteURL(const std::string& utf8_text) {
+ DCHECK(CalledOnValidThread());
+
+ aurax11_details_->CreateNewClipboardData();
+ WriteText(utf8_text.c_str(), utf8_text.size());
+ aurax11_details_->TakeOwnershipOfSelection(BUFFER_SELECTION);
}
uint64 Clipboard::GetSequenceNumber(Buffer buffer) {
DCHECK(CalledOnValidThread());
- return GetClipboard()->GetNumClipboardEntries();
+ if (buffer == BUFFER_STANDARD)
+ return SelectionChangeObserver::GetInstance()->clipboard_sequence_number();
+ else
+ return SelectionChangeObserver::GetInstance()->primary_sequence_number();
}
void Clipboard::WriteText(const char* text_data, size_t text_len) {
- ClipboardDataBuilder::WriteText(text_data, text_len);
+ char* data = new char[text_len];
+ memcpy(data, text_data, text_len);
+
+ aurax11_details_->InsertMapping(kMimeTypeText, data, text_len);
+ aurax11_details_->InsertMapping(kText, data, text_len);
+ aurax11_details_->InsertMapping(kString, data, text_len);
+ aurax11_details_->InsertMapping(kUtf8String, data, text_len);
}
void Clipboard::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);
+ // TODO(estade): We need to expand relative links with |url_data|.
+ static const char* html_prefix = "<meta http-equiv=\"content-type\" "
+ "content=\"text/html; charset=utf-8\">";
+ size_t html_prefix_len = strlen(html_prefix);
+ size_t total_len = html_prefix_len + markup_len + 1;
+
+ char* data = new char[total_len];
+ snprintf(data, total_len, "%s", html_prefix);
+ memcpy(data + html_prefix_len, markup_data, markup_len);
+ // Some programs expect NULL-terminated data. See http://crbug.com/42624
+ data[total_len - 1] = '\0';
+
+ aurax11_details_->InsertMapping(kMimeTypeHTML, data, total_len);
}
void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) {
- ClipboardDataBuilder::WriteRTF(rtf_data, data_len);
+ WriteData(GetRtfFormatType(), rtf_data, data_len);
}
void Clipboard::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);
+ // Write as a mozilla url (UTF16: URL, newline, title).
+ string16 url = UTF8ToUTF16(std::string(url_data, url_len) + "\n");
+ string16 title = UTF8ToUTF16(std::string(title_data, title_len));
+ int data_len = 2 * (title.length() + url.length());
+
+ char* data = new char[data_len];
+ memcpy(data, url.data(), 2 * url.length());
+ memcpy(data + 2 * url.length(), title.data(), 2 * title.length());
+ aurax11_details_->InsertMapping(kMimeTypeMozillaURL, data, data_len);
}
+// Write an extra flavor that signifies WebKit was the last to modify the
+// pasteboard. This flavor has no data.
void Clipboard::WriteWebSmartPaste() {
- ClipboardDataBuilder::WriteWebSmartPaste();
+ aurax11_details_->InsertMapping(kMimeTypeWebkitSmartPaste, NULL, 0);
}
void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) {
- ClipboardDataBuilder::WriteBitmap(pixel_data, size_data);
+ // TODO(erg): I'm not sure if we should be writting BMP data here or
+ // not. It's what the GTK port does, but I'm not sure it's the right thing to
+ // do.
+ NOTIMPLEMENTED();
}
void Clipboard::WriteData(const FormatType& format,
const char* data_data,
size_t data_len) {
- ClipboardDataBuilder::WriteData(format.ToString(), data_data, data_len);
+ // We assume that certain mapping types are only written by trusted code.
+ // Therefore we must upkeep their integrity.
+ if (format.Equals(GetBitmapFormatType()))
+ return;
+ char* data = new char[data_len];
+ memcpy(data, data_data, data_len);
+ aurax11_details_->InsertMapping(format.ToString(), data, data_len);
}
// static
« no previous file with comments | « ui/base/clipboard/clipboard.h ('k') | ui/base/clipboard/clipboard_chromeos.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698