| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/base/clipboard/clipboard.h" | 5 #include "ui/base/clipboard/clipboard.h" |
| 6 | 6 |
| 7 #include <X11/extensions/Xfixes.h> |
| 8 #include <X11/Xatom.h> |
| 7 #include <list> | 9 #include <list> |
| 10 #include <set> |
| 8 | 11 |
| 9 #include "base/basictypes.h" | 12 #include "base/basictypes.h" |
| 10 #include "base/file_path.h" | 13 #include "base/file_path.h" |
| 14 #include "base/i18n/icu_string_conversions.h" |
| 11 #include "base/logging.h" | 15 #include "base/logging.h" |
| 12 #include "base/memory/scoped_ptr.h" | 16 #include "base/memory/scoped_ptr.h" |
| 17 #include "base/memory/singleton.h" |
| 18 #include "base/message_pump_aurax11.h" |
| 19 #include "base/message_pump_observer.h" |
| 20 #include "base/run_loop.h" |
| 13 #include "base/stl_util.h" | 21 #include "base/stl_util.h" |
| 14 #include "base/utf_string_conversions.h" | 22 #include "base/utf_string_conversions.h" |
| 15 #include "third_party/skia/include/core/SkBitmap.h" | 23 #include "third_party/skia/include/core/SkBitmap.h" |
| 16 #include "ui/base/clipboard/custom_data_helper.h" | 24 #include "ui/base/clipboard/custom_data_helper.h" |
| 25 #include "ui/base/x/x11_atom_cache.h" |
| 26 #include "ui/base/x/x11_util.h" |
| 27 |
| 17 #include "ui/gfx/size.h" | 28 #include "ui/gfx/size.h" |
| 18 | 29 |
| 19 namespace ui { | 30 namespace ui { |
| 20 | 31 |
| 21 namespace { | 32 namespace { |
| 33 |
| 34 const char kChromeSelection[] = "CHROME_SELECTION"; |
| 35 const char kClipboard[] = "CLIPBOARD"; |
| 36 const char kMimeTypeBitmap[] = "image/bmp"; |
| 22 const char kMimeTypeFilename[] = "chromium/filename"; | 37 const char kMimeTypeFilename[] = "chromium/filename"; |
| 23 const char kMimeTypeBitmap[] = "image/bmp"; | 38 const char kMimeTypeMozillaURL[] = "text/x-moz-url"; |
| 24 const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste"; | 39 const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste"; |
| 25 const size_t kMaxClipboardSize = 1; | 40 const char kMultiple[] = "MULTIPLE"; |
| 26 | 41 const char kString[] = "STRING"; |
| 27 // Clipboard data format used by AuraClipboard. | 42 const char kTargets[] = "TARGETS"; |
| 28 enum AuraClipboardFormat { | 43 const char kText[] = "TEXT"; |
| 29 TEXT = 1 << 0, | 44 const char kUtf8String[] = "UTF8_STRING"; |
| 30 HTML = 1 << 1, | 45 |
| 31 RTF = 1 << 2, | 46 const char* kAtomsToCache[] = { |
| 32 BOOKMARK = 1 << 3, | 47 kChromeSelection, |
| 33 BITMAP = 1 << 4, | 48 kClipboard, |
| 34 CUSTOM = 1 << 5, | 49 kMimeTypeBitmap, |
| 35 WEB = 1 << 6, | 50 kMimeTypeFilename, |
| 51 kMimeTypeMozillaURL, |
| 52 kMimeTypeWebkitSmartPaste, |
| 53 kMultiple, |
| 54 kString, |
| 55 kTargets, |
| 56 kText, |
| 57 kUtf8String, |
| 58 NULL |
| 36 }; | 59 }; |
| 37 | 60 |
| 38 // ClipboardData contains data copied to the Clipboard for a variety of formats. | 61 /////////////////////////////////////////////////////////////////////////////// |
| 39 // It mostly just provides APIs to cleanly access and manipulate this data. | 62 |
| 40 class ClipboardData { | 63 // Returns a list of all text atoms that we handle. |
| 64 std::vector< ::Atom> GetTextAtomsFrom(const X11AtomCache* atom_cache) { |
| 65 std::vector< ::Atom> atoms; |
| 66 atoms.push_back(atom_cache->GetAtom(kUtf8String)); |
| 67 atoms.push_back(atom_cache->GetAtom(kString)); |
| 68 atoms.push_back(atom_cache->GetAtom(kText)); |
| 69 return atoms; |
| 70 } |
| 71 |
| 72 /////////////////////////////////////////////////////////////////////////////// |
| 73 |
| 74 // Uses the XFixes API to provide sequence numbers for GetSequenceNumber(). |
| 75 class SelectionChangeObserver : public base::MessagePumpObserver { |
| 41 public: | 76 public: |
| 42 ClipboardData() | 77 static SelectionChangeObserver* GetInstance(); |
| 43 : bitmap_data_(), | 78 |
| 44 web_smart_paste_(false), | 79 uint64 clipboard_sequence_number() const { |
| 45 format_(0) {} | 80 return clipboard_sequence_number_; |
| 46 | 81 } |
| 47 virtual ~ClipboardData() {} | 82 uint64 primary_sequence_number() const { return primary_sequence_number_; } |
| 48 | 83 |
| 49 // Bitmask of AuraClipboardFormat types. | 84 private: |
| 50 const int format() const { return format_; } | 85 friend struct DefaultSingletonTraits<SelectionChangeObserver>; |
| 51 | 86 |
| 52 const std::string& text() const { return text_; } | 87 SelectionChangeObserver(); |
| 53 void set_text(const std::string& text) { | 88 ~SelectionChangeObserver(); |
| 54 text_ = text; | 89 |
| 55 format_ |= TEXT; | 90 // Overridden from base::MessagePumpObserver: |
| 56 } | 91 virtual base::EventStatus WillProcessEvent( |
| 57 | 92 const base::NativeEvent& event) OVERRIDE; |
| 58 const std::string& markup_data() const { return markup_data_; } | 93 virtual void DidProcessEvent( |
| 59 void set_markup_data(const std::string& markup_data) { | 94 const base::NativeEvent& event) OVERRIDE {} |
| 60 markup_data_ = markup_data; | 95 |
| 61 format_ |= HTML; | 96 int event_base_; |
| 62 } | 97 Atom clipboard_atom_; |
| 63 | 98 uint64 clipboard_sequence_number_; |
| 64 const std::string& rtf_data() const { return rtf_data_; } | 99 uint64 primary_sequence_number_; |
| 65 void SetRTFData(const std::string& rtf_data) { | 100 |
| 66 rtf_data_ = rtf_data; | 101 DISALLOW_COPY_AND_ASSIGN(SelectionChangeObserver); |
| 67 format_ |= RTF; | 102 }; |
| 68 } | 103 |
| 69 | 104 SelectionChangeObserver::SelectionChangeObserver() |
| 70 const std::string& url() const { return url_; } | 105 : event_base_(-1), |
| 71 void set_url(const std::string& url) { | 106 clipboard_atom_(None), |
| 72 url_ = url; | 107 clipboard_sequence_number_(0), |
| 73 format_ |= HTML; | 108 primary_sequence_number_(0) { |
| 74 } | 109 int ignored; |
| 75 | 110 if (XFixesQueryExtension(GetXDisplay(), &event_base_, &ignored)) { |
| 76 const std::string& bookmark_title() const { return bookmark_title_; } | 111 clipboard_atom_ = XInternAtom(GetXDisplay(), kClipboard, false); |
| 77 void set_bookmark_title(const std::string& bookmark_title) { | 112 XFixesSelectSelectionInput(GetXDisplay(), GetX11RootWindow(), |
| 78 bookmark_title_ = bookmark_title; | 113 clipboard_atom_, |
| 79 format_ |= BOOKMARK; | 114 XFixesSetSelectionOwnerNotifyMask | |
| 80 } | 115 XFixesSelectionWindowDestroyNotifyMask | |
| 81 | 116 XFixesSelectionClientCloseNotifyMask); |
| 82 const std::string& bookmark_url() const { return bookmark_url_; } | 117 // This seems to be semi-optional. For some reason, registering for any |
| 83 void set_bookmark_url(const std::string& bookmark_url) { | 118 // selection notify events seems to subscribe us to events for both the |
| 84 bookmark_url_ = bookmark_url; | 119 // primary and the clipboard buffers. Register anyway just to be safe. |
| 85 format_ |= BOOKMARK; | 120 XFixesSelectSelectionInput(GetXDisplay(), GetX11RootWindow(), |
| 86 } | 121 XA_PRIMARY, |
| 87 | 122 XFixesSetSelectionOwnerNotifyMask | |
| 88 uint8_t* bitmap_data() const { return bitmap_data_.get(); } | 123 XFixesSelectionWindowDestroyNotifyMask | |
| 89 const gfx::Size& bitmap_size() const { return bitmap_size_; } | 124 XFixesSelectionClientCloseNotifyMask); |
| 90 void SetBitmapData(const char* pixel_data, const char* size_data) { | 125 |
| 91 bitmap_size_ = *reinterpret_cast<const gfx::Size*>(size_data); | 126 base::MessagePumpAuraX11::Current()->AddObserver(this); |
| 92 | 127 } |
| 93 // We assume 4-byte pixel data. | 128 } |
| 94 size_t bitmap_data_len = 4 * bitmap_size_.width() * bitmap_size_.height(); | 129 |
| 95 bitmap_data_.reset(new uint8_t[bitmap_data_len]); | 130 SelectionChangeObserver::~SelectionChangeObserver() { |
| 96 memcpy(bitmap_data_.get(), pixel_data, bitmap_data_len); | 131 // We are a singleton; we will outlive our message pump. |
| 97 format_ |= BITMAP; | 132 } |
| 98 } | 133 |
| 99 | 134 SelectionChangeObserver* SelectionChangeObserver::GetInstance() { |
| 100 const std::string& custom_data_format() const { return custom_data_format_; } | 135 return Singleton<SelectionChangeObserver>::get(); |
| 101 const std::string& custom_data_data() const { return custom_data_data_; } | 136 } |
| 102 void SetCustomData(const std::string& data_format, | 137 |
| 103 const std::string& data_data) { | 138 base::EventStatus SelectionChangeObserver::WillProcessEvent( |
| 104 if (data_data.size() == 0) { | 139 const base::NativeEvent& event) { |
| 105 custom_data_data_.clear(); | 140 if (event->type == event_base_ + XFixesSelectionNotify) { |
| 106 custom_data_format_.clear(); | 141 XFixesSelectionNotifyEvent* ev = |
| 107 return; | 142 reinterpret_cast<XFixesSelectionNotifyEvent*>(event); |
| 143 if (ev->selection == clipboard_atom_) { |
| 144 clipboard_sequence_number_++; |
| 145 } else if (ev->selection == XA_PRIMARY) { |
| 146 primary_sequence_number_++; |
| 147 } else { |
| 148 DLOG(ERROR) << "Unexpected selection atom: " << ev->selection; |
| 108 } | 149 } |
| 109 custom_data_data_ = data_data; | 150 } |
| 110 custom_data_format_ = data_format; | 151 return base::EVENT_CONTINUE; |
| 111 format_ |= CUSTOM; | 152 } |
| 112 } | 153 |
| 113 | 154 /////////////////////////////////////////////////////////////////////////////// |
| 114 bool web_smart_paste() const { return web_smart_paste_; } | 155 |
| 115 void set_web_smart_paste(bool web_smart_paste) { | 156 // Represents the selection in different data formats. Binary data passed in is |
| 116 web_smart_paste_ = web_smart_paste; | 157 // assumed to be allocated with new char[], and is owned by FormatMap. |
| 117 format_ |= WEB; | 158 class FormatMap { |
| 118 } | 159 public: |
| 160 // Our internal data store, which we only expose through iterators. |
| 161 typedef std::map< ::Atom, std::pair<char*, size_t> > InternalMap; |
| 162 typedef std::map< ::Atom, std::pair<char*, size_t> >::const_iterator |
| 163 const_iterator; |
| 164 |
| 165 FormatMap(); |
| 166 ~FormatMap(); |
| 167 |
| 168 // Adds the selection in the format |atom|. Ownership of |data| is passed to |
| 169 // us. |
| 170 void Insert(::Atom atom, char* data, size_t size); |
| 171 |
| 172 // Pass through to STL map. Only allow non-mutation access. |
| 173 const_iterator begin() const { return data_.begin(); } |
| 174 const_iterator end() const { return data_.end(); } |
| 175 const_iterator find(::Atom atom) const { return data_.find(atom); } |
| 176 size_t size() const { return data_.size(); } |
| 119 | 177 |
| 120 private: | 178 private: |
| 121 // Plain text in UTF8 format. | 179 InternalMap data_; |
| 122 std::string text_; | 180 |
| 123 | 181 DISALLOW_COPY_AND_ASSIGN(FormatMap); |
| 124 // HTML markup data in UTF8 format. | |
| 125 std::string markup_data_; | |
| 126 std::string url_; | |
| 127 | |
| 128 // RTF data. | |
| 129 std::string rtf_data_; | |
| 130 | |
| 131 // Bookmark title in UTF8 format. | |
| 132 std::string bookmark_title_; | |
| 133 std::string bookmark_url_; | |
| 134 | |
| 135 // Filenames. | |
| 136 std::vector<std::string> files_; | |
| 137 | |
| 138 // Bitmap images. | |
| 139 scoped_array<uint8_t> bitmap_data_; | |
| 140 gfx::Size bitmap_size_; | |
| 141 | |
| 142 // Data with custom format. | |
| 143 std::string custom_data_format_; | |
| 144 std::string custom_data_data_; | |
| 145 | |
| 146 // WebKit smart paste data. | |
| 147 bool web_smart_paste_; | |
| 148 | |
| 149 int format_; | |
| 150 | |
| 151 DISALLOW_COPY_AND_ASSIGN(ClipboardData); | |
| 152 }; | 182 }; |
| 153 | 183 |
| 154 // Platform clipboard implementation for Aura. This handles things like format | 184 FormatMap::FormatMap() {} |
| 155 // conversion, versioning of clipboard items etc. The goal is to roughly provide | 185 |
| 156 // a substitute to platform clipboards on other platforms such as GtkClipboard | 186 FormatMap::~FormatMap() { |
| 157 // on gtk or winapi clipboard on win. | 187 // WriteText() inserts the same pointer multiple times for different |
| 158 class AuraClipboard { | 188 // representations; we need to dedupe it. |
| 189 std::set<char*> to_delete; |
| 190 for (InternalMap::iterator it = data_.begin(); it != data_.end(); ++it) |
| 191 to_delete.insert(it->second.first); |
| 192 |
| 193 for (std::set<char*>::iterator it = to_delete.begin(); it != to_delete.end(); |
| 194 ++it) { |
| 195 delete [] *it; |
| 196 } |
| 197 } |
| 198 |
| 199 void FormatMap::Insert(::Atom atom, char* data, size_t size) { |
| 200 DCHECK(data_.find(atom) == data_.end()); |
| 201 data_.insert(std::make_pair(atom, std::make_pair(data, size))); |
| 202 } |
| 203 |
| 204 /////////////////////////////////////////////////////////////////////////////// |
| 205 |
| 206 // Represents a list of possible return types. Copy constructable. |
| 207 class TargetList { |
| 159 public: | 208 public: |
| 160 AuraClipboard() {} | 209 typedef std::vector< ::Atom> AtomVector; |
| 161 | 210 |
| 162 ~AuraClipboard() { | 211 TargetList(const AtomVector& target_list, X11AtomCache* atom_cache); |
| 163 Clear(); | 212 |
| 164 } | 213 bool ContainsText() const; |
| 165 | 214 bool ContainsFormat(const Clipboard::FormatType& format_type) const; |
| 166 void Clear() { | 215 bool ContainsAtom(::Atom atom) const; |
| 167 STLDeleteContainerPointers(data_list_.begin(), data_list_.end()); | |
| 168 data_list_.clear(); | |
| 169 } | |
| 170 | |
| 171 // Returns the number of entries currently in the clipboard stack. | |
| 172 size_t GetNumClipboardEntries() { | |
| 173 return data_list_.size(); | |
| 174 } | |
| 175 | |
| 176 // Returns the data currently on the top of the clipboard stack, NULL if the | |
| 177 // clipboard stack is empty. | |
| 178 const ClipboardData* GetData() const { | |
| 179 if (data_list_.empty()) | |
| 180 return NULL; | |
| 181 return data_list_.front(); | |
| 182 } | |
| 183 | |
| 184 // Returns true if the data on top of the clipboard stack has format |format| | |
| 185 // or another format that can be converted to |format|. | |
| 186 bool IsFormatAvailable(AuraClipboardFormat format) const { | |
| 187 switch (format) { | |
| 188 case TEXT: | |
| 189 return HasFormat(TEXT) || HasFormat(BOOKMARK); | |
| 190 default: | |
| 191 return HasFormat(format); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 // Reads text from the data at the top of clipboard stack. | |
| 196 void ReadText(string16* result) const { | |
| 197 std::string utf8_result; | |
| 198 ReadAsciiText(&utf8_result); | |
| 199 *result = UTF8ToUTF16(utf8_result); | |
| 200 } | |
| 201 | |
| 202 // Reads ascii text from the data at the top of clipboard stack. | |
| 203 void ReadAsciiText(std::string* result) const { | |
| 204 result->clear(); | |
| 205 const ClipboardData* data = GetData(); | |
| 206 if (!data) | |
| 207 return; | |
| 208 if (HasFormat(TEXT)) | |
| 209 *result = data->text(); | |
| 210 else if (HasFormat(HTML)) | |
| 211 *result = data->markup_data(); | |
| 212 else if (HasFormat(BOOKMARK)) | |
| 213 *result = data->bookmark_url(); | |
| 214 } | |
| 215 | |
| 216 // Reads HTML from the data at the top of clipboard stack. | |
| 217 void ReadHTML(string16* markup, | |
| 218 std::string* src_url, | |
| 219 uint32* fragment_start, | |
| 220 uint32* fragment_end) const { | |
| 221 markup->clear(); | |
| 222 if (src_url) | |
| 223 src_url->clear(); | |
| 224 *fragment_start = 0; | |
| 225 *fragment_end = 0; | |
| 226 | |
| 227 if (!HasFormat(HTML)) | |
| 228 return; | |
| 229 | |
| 230 const ClipboardData* data = GetData(); | |
| 231 *markup = UTF8ToUTF16(data->markup_data()); | |
| 232 *src_url = data->url(); | |
| 233 | |
| 234 *fragment_start = 0; | |
| 235 DCHECK_LE(markup->length(), kuint32max); | |
| 236 *fragment_end = static_cast<uint32>(markup->length()); | |
| 237 } | |
| 238 | |
| 239 // Reads RTF from the data at the top of clipboard stack. | |
| 240 void ReadRTF(std::string* result) const { | |
| 241 result->clear(); | |
| 242 const ClipboardData* data = GetData(); | |
| 243 if (!HasFormat(RTF)) | |
| 244 return; | |
| 245 | |
| 246 *result = data->rtf_data(); | |
| 247 } | |
| 248 | |
| 249 // Reads image from the data at the top of clipboard stack. | |
| 250 SkBitmap ReadImage() const { | |
| 251 SkBitmap img; | |
| 252 if (!HasFormat(BITMAP)) | |
| 253 return img; | |
| 254 | |
| 255 const ClipboardData* data = GetData(); | |
| 256 const gfx::Size size = data->bitmap_size(); | |
| 257 uint8_t* bitmap = data->bitmap_data(); | |
| 258 img.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height(), 0); | |
| 259 img.allocPixels(); | |
| 260 img.eraseARGB(0, 0, 0, 0); | |
| 261 memcpy(img.getPixels(), bitmap, size.width() * size.height() * 4); | |
| 262 return img; | |
| 263 } | |
| 264 | |
| 265 // Reads data of type |type| from the data at the top of clipboard stack. | |
| 266 void ReadCustomData(const string16& type, string16* result) const { | |
| 267 result->clear(); | |
| 268 const ClipboardData* data = GetData(); | |
| 269 if (!HasFormat(CUSTOM)) | |
| 270 return; | |
| 271 | |
| 272 ui::ReadCustomDataForType(data->custom_data_data().c_str(), | |
| 273 data->custom_data_data().size(), | |
| 274 type, result); | |
| 275 } | |
| 276 | |
| 277 // Reads bookmark from the data at the top of clipboard stack. | |
| 278 void ReadBookmark(string16* title, std::string* url) const { | |
| 279 title->clear(); | |
| 280 url->clear(); | |
| 281 if (!HasFormat(BOOKMARK)) | |
| 282 return; | |
| 283 | |
| 284 const ClipboardData* data = GetData(); | |
| 285 *title = UTF8ToUTF16(data->bookmark_title()); | |
| 286 *url = data->bookmark_url(); | |
| 287 } | |
| 288 | |
| 289 void ReadData(const std::string& type, std::string* result) const { | |
| 290 result->clear(); | |
| 291 const ClipboardData* data = GetData(); | |
| 292 if (!HasFormat(CUSTOM) || type != data->custom_data_format()) | |
| 293 return; | |
| 294 | |
| 295 *result = data->custom_data_data(); | |
| 296 } | |
| 297 | |
| 298 // Writes |data| to the top of the clipboard stack. | |
| 299 void WriteData(ClipboardData* data) { | |
| 300 DCHECK(data); | |
| 301 AddToListEnsuringSize(data); | |
| 302 } | |
| 303 | 216 |
| 304 private: | 217 private: |
| 305 // True if the data on top of the clipboard stack has format |format|. | 218 AtomVector target_list_; |
| 306 bool HasFormat(AuraClipboardFormat format) const { | 219 X11AtomCache* atom_cache_; |
| 307 const ClipboardData* data = GetData(); | |
| 308 if (!data) | |
| 309 return false; | |
| 310 | |
| 311 return data->format() & format; | |
| 312 } | |
| 313 | |
| 314 void AddToListEnsuringSize(ClipboardData* data) { | |
| 315 DCHECK(data); | |
| 316 data_list_.push_front(data); | |
| 317 | |
| 318 // If the size of list becomes more than the maximum allowed, we delete the | |
| 319 // last element. | |
| 320 if (data_list_.size() > kMaxClipboardSize) { | |
| 321 ClipboardData* last = data_list_.back(); | |
| 322 data_list_.pop_back(); | |
| 323 delete last; | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 // Stack containing various versions of ClipboardData. | |
| 328 std::list<ClipboardData*> data_list_; | |
| 329 | |
| 330 DISALLOW_COPY_AND_ASSIGN(AuraClipboard); | |
| 331 }; | 220 }; |
| 332 | 221 |
| 333 AuraClipboard* aura_clipboard = NULL; | 222 TargetList::TargetList(const AtomVector& target_list, |
| 334 | 223 X11AtomCache* atom_cache) |
| 335 AuraClipboard* GetClipboard() { | 224 : target_list_(target_list), |
| 336 if (!aura_clipboard) | 225 atom_cache_(atom_cache) { |
| 337 aura_clipboard = new AuraClipboard(); | 226 } |
| 338 return aura_clipboard; | 227 |
| 339 } | 228 bool TargetList::ContainsText() const { |
| 340 | 229 std::vector< ::Atom> atoms = GetTextAtomsFrom(atom_cache_); |
| 341 void DeleteClipboard() { | 230 for (std::vector< ::Atom>::const_iterator it = atoms.begin(); |
| 342 if (aura_clipboard) | 231 it != atoms.end(); ++it) { |
| 343 delete aura_clipboard; | 232 if (ContainsAtom(*it)) |
| 344 aura_clipboard = NULL; | 233 return true; |
| 345 } | 234 } |
| 346 | 235 |
| 347 // Helper class to build a ClipboardData object and write it to clipboard. | 236 return false; |
| 348 class ClipboardDataBuilder { | 237 } |
| 238 |
| 239 bool TargetList::ContainsFormat( |
| 240 const Clipboard::FormatType& format_type) const { |
| 241 ::Atom atom = atom_cache_->GetAtom(format_type.ToString().c_str()); |
| 242 return ContainsAtom(atom); |
| 243 } |
| 244 |
| 245 bool TargetList::ContainsAtom(::Atom atom) const { |
| 246 return find(target_list_.begin(), target_list_.end(), atom) |
| 247 != target_list_.end(); |
| 248 } |
| 249 |
| 250 /////////////////////////////////////////////////////////////////////////////// |
| 251 |
| 252 // A holder for data with optional X11 deletion semantics. |
| 253 class SelectionData { |
| 349 public: | 254 public: |
| 350 static void CommitToClipboard() { | 255 // |atom_cache| is still owned by caller. |
| 351 DCHECK(current_data_); | 256 explicit SelectionData(X11AtomCache* atom_cache); |
| 352 GetClipboard()->WriteData(current_data_); | 257 ~SelectionData(); |
| 353 current_data_ = NULL; | 258 |
| 354 } | 259 ::Atom type() const { return type_; } |
| 355 | 260 char* data() const { return data_; } |
| 356 static void WriteText(const char* text_data, size_t text_len) { | 261 size_t size() const { return size_; } |
| 357 ClipboardData* data = GetCurrentData(); | 262 |
| 358 data->set_text(std::string(text_data, text_len)); | 263 void Set(::Atom type, char* data, size_t size, bool owned); |
| 359 } | 264 |
| 360 | 265 // If |type_| is a string type, convert the data to UTF8 and return it. |
| 361 static void WriteHTML(const char* markup_data, | 266 std::string GetText() const; |
| 362 size_t markup_len, | 267 |
| 363 const char* url_data, | 268 // Assigns the raw data to the string. |
| 364 size_t url_len) { | 269 void AssignTo(std::string* result) const; |
| 365 ClipboardData* data = GetCurrentData(); | |
| 366 data->set_markup_data(std::string(markup_data, markup_len)); | |
| 367 data->set_url(std::string(url_data, url_len)); | |
| 368 } | |
| 369 | |
| 370 static void WriteRTF(const char* rtf_data, size_t rtf_len) { | |
| 371 ClipboardData* data = GetCurrentData(); | |
| 372 data->SetRTFData(std::string(rtf_data, rtf_len)); | |
| 373 } | |
| 374 | |
| 375 static void WriteBookmark(const char* title_data, | |
| 376 size_t title_len, | |
| 377 const char* url_data, | |
| 378 size_t url_len) { | |
| 379 ClipboardData* data = GetCurrentData(); | |
| 380 data->set_bookmark_title(std::string(title_data, title_len)); | |
| 381 data->set_bookmark_url(std::string(url_data, url_len)); | |
| 382 } | |
| 383 | |
| 384 static void WriteWebSmartPaste() { | |
| 385 ClipboardData* data = GetCurrentData(); | |
| 386 data->set_web_smart_paste(true); | |
| 387 } | |
| 388 | |
| 389 static void WriteBitmap(const char* pixel_data, const char* size_data) { | |
| 390 ClipboardData* data = GetCurrentData(); | |
| 391 data->SetBitmapData(pixel_data, size_data); | |
| 392 } | |
| 393 | |
| 394 static void WriteData(const std::string& format, | |
| 395 const char* data_data, | |
| 396 size_t data_len) { | |
| 397 ClipboardData* data = GetCurrentData(); | |
| 398 data->SetCustomData(format, std::string(data_data, data_len)); | |
| 399 } | |
| 400 | 270 |
| 401 private: | 271 private: |
| 402 static ClipboardData* GetCurrentData() { | 272 ::Atom type_; |
| 403 if (!current_data_) | 273 char* data_; |
| 404 current_data_ = new ClipboardData; | 274 size_t size_; |
| 405 return current_data_; | 275 bool owned_; |
| 406 } | 276 |
| 407 | 277 X11AtomCache* atom_cache_; |
| 408 static ClipboardData* current_data_; | |
| 409 }; | 278 }; |
| 410 | 279 |
| 411 ClipboardData* ClipboardDataBuilder::current_data_ = NULL; | 280 SelectionData::SelectionData(X11AtomCache* atom_cache) |
| 281 : type_(None), |
| 282 data_(NULL), |
| 283 size_(0), |
| 284 owned_(false), |
| 285 atom_cache_(atom_cache) { |
| 286 } |
| 287 |
| 288 SelectionData::~SelectionData() { |
| 289 if (owned_) |
| 290 XFree(data_); |
| 291 } |
| 292 |
| 293 void SelectionData::Set(::Atom type, char* data, size_t size, bool owned) { |
| 294 if (owned_) |
| 295 XFree(data_); |
| 296 |
| 297 type_ = type; |
| 298 data_ = data; |
| 299 size_ = size; |
| 300 owned_ = owned; |
| 301 } |
| 302 |
| 303 std::string SelectionData::GetText() const { |
| 304 if (type_ == atom_cache_->GetAtom(kUtf8String) || |
| 305 type_ == atom_cache_->GetAtom(kText)) { |
| 306 return std::string(data_, size_); |
| 307 } else if (type_ == atom_cache_->GetAtom(kString)) { |
| 308 std::string result; |
| 309 base::ConvertToUtf8AndNormalize(std::string(data_, size_), |
| 310 base::kCodepageLatin1, |
| 311 &result); |
| 312 return result; |
| 313 } else { |
| 314 // BTW, I looked at COMPOUND_TEXT, and there's no way we're going to |
| 315 // support that. Yuck. |
| 316 NOTREACHED(); |
| 317 return std::string(); |
| 318 } |
| 319 } |
| 320 |
| 321 void SelectionData::AssignTo(std::string* result) const { |
| 322 result->assign(data_, size_); |
| 323 } |
| 412 | 324 |
| 413 } // namespace | 325 } // namespace |
| 414 | 326 |
| 327 /////////////////////////////////////////////////////////////////////////////// |
| 328 |
| 329 // I would love for the FormatType to really be a wrapper around an X11 ::Atom, |
| 330 // but there are a few problems. Chromeos unit tests spawn a new X11 server for |
| 331 // each test, so Atom numeric values don't persist across tests. We could still |
| 332 // maybe deal with that if we didn't have static accessor methods everywhere. |
| 333 |
| 415 Clipboard::FormatType::FormatType() { | 334 Clipboard::FormatType::FormatType() { |
| 416 } | 335 } |
| 417 | 336 |
| 418 Clipboard::FormatType::FormatType(const std::string& native_format) | 337 Clipboard::FormatType::FormatType(const std::string& native_format) |
| 419 : data_(native_format) { | 338 : data_(native_format) { |
| 420 } | 339 } |
| 421 | 340 |
| 422 Clipboard::FormatType::~FormatType() { | 341 Clipboard::FormatType::~FormatType() { |
| 423 } | 342 } |
| 424 | 343 |
| 425 std::string Clipboard::FormatType::Serialize() const { | 344 std::string Clipboard::FormatType::Serialize() const { |
| 426 return data_; | 345 return data_; |
| 427 } | 346 } |
| 428 | 347 |
| 429 // static | 348 // static |
| 430 Clipboard::FormatType Clipboard::FormatType::Deserialize( | 349 Clipboard::FormatType Clipboard::FormatType::Deserialize( |
| 431 const std::string& serialization) { | 350 const std::string& serialization) { |
| 432 return FormatType(serialization); | 351 return FormatType(serialization); |
| 433 } | 352 } |
| 434 | 353 |
| 435 bool Clipboard::FormatType::Equals(const FormatType& other) const { | 354 bool Clipboard::FormatType::Equals(const FormatType& other) const { |
| 436 return data_ == other.data_; | 355 return data_ == other.data_; |
| 437 } | 356 } |
| 438 | 357 |
| 439 Clipboard::Clipboard() { | 358 /////////////////////////////////////////////////////////////////////////////// |
| 359 // Clipboard::AuraX11Details |
| 360 |
| 361 // Private implementation of our X11 integration. Keeps X11 headers out of the |
| 362 // majority of chrome, which break badly. |
| 363 class Clipboard::AuraX11Details : public base::MessagePumpDispatcher { |
| 364 public: |
| 365 AuraX11Details(); |
| 366 ~AuraX11Details(); |
| 367 |
| 368 X11AtomCache* atom_cache() { return &atom_cache_; } |
| 369 |
| 370 // Returns the X11 type that we pass to various XSelection functions for the |
| 371 // given buffer. |
| 372 ::Atom LookupSelectionForBuffer(Buffer buffer) const; |
| 373 |
| 374 // Finds the FormatMap for the incoming selection atom. |
| 375 FormatMap* LookupStorageForAtom(::Atom atom); |
| 376 |
| 377 // As we need to collect all the data types before we tell X11 that we own a |
| 378 // particular selection, we create a temporary clipboard mapping that |
| 379 // InsertMapping writes to. Then we commit it in TakeOwnershipOfSelection, |
| 380 // where we save it in one of the clipboard data slots. |
| 381 void CreateNewClipboardData(); |
| 382 |
| 383 // Inserts a mapping into clipboard_data_. |
| 384 void InsertMapping(const std::string& key, char* data, size_t data_len); |
| 385 |
| 386 // Moves the temporary |clipboard_data_| to the long term data storage for |
| 387 // |buffer|. |
| 388 void TakeOwnershipOfSelection(Buffer buffer); |
| 389 |
| 390 // Returns the first of |types| offered by the current selection holder in |
| 391 // |data_out|, or returns NULL if none of those types are available. |
| 392 // |
| 393 // If the selection holder is us, this call is synchronous and we pull |
| 394 // the data out of |clipboard_selection_| or |primary_selection_|. If the |
| 395 // selection holder is some other window, we spin up a nested message loop |
| 396 // and do the asynchronous dance with whatever application is holding the |
| 397 // selection. |
| 398 scoped_ptr<SelectionData> RequestAndWaitForTypes( |
| 399 Buffer buffer, |
| 400 const std::vector< ::Atom>& types); |
| 401 |
| 402 // Retrieves the list of possible data types the current clipboard owner has. |
| 403 // |
| 404 // If the selection holder is us, this is synchronous, otherwise this runs a |
| 405 // blocking message loop. |
| 406 TargetList WaitAndGetTargetsList(Buffer buffer); |
| 407 |
| 408 // Does the work of requesting |target| from |selection_name|, spinning up |
| 409 // the nested message loop, and reading the resulting data back. |out_data| |
| 410 // is allocated with the X allocator and must be freed with |
| 411 // XFree(). |out_data_bytes| is the length in machine chars, while |
| 412 // |out_data_items| is the length in |out_type| items. |
| 413 bool PerformBlockingConvertSelection(::Atom selection_name, |
| 414 ::Atom target, |
| 415 unsigned char** out_data, |
| 416 size_t* out_data_bytes, |
| 417 size_t* out_data_items, |
| 418 ::Atom* out_type); |
| 419 |
| 420 // Returns a list of all text atoms that we handle. |
| 421 std::vector< ::Atom> GetTextAtoms() const; |
| 422 |
| 423 // Returns a vector with a |format| converted to an X11 atom. |
| 424 std::vector< ::Atom> GetAtomsForFormat(const Clipboard::FormatType& format); |
| 425 |
| 426 // Clears a certain data buffer. |
| 427 void Clear(Buffer buffer); |
| 428 |
| 429 private: |
| 430 // Called by Dispatch to handle specific types of events. |
| 431 void HandleSelectionRequest(const XSelectionRequestEvent& event); |
| 432 void HandleSelectionNotify(const XSelectionEvent& event); |
| 433 void HandleSelectionClear(const XSelectionClearEvent& event); |
| 434 void HandlePropertyNotify(const XPropertyEvent& event); |
| 435 |
| 436 // Overridden from base::MessagePumpDispatcher: |
| 437 virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; |
| 438 |
| 439 // Temporary target map that we write to during DispatchObects. |
| 440 scoped_ptr<FormatMap> clipboard_data_; |
| 441 |
| 442 // The current value of our clipboard and primary selections. These should be |
| 443 // non-NULL when we own the selection. |
| 444 scoped_ptr<FormatMap> clipboard_selection_; |
| 445 scoped_ptr<FormatMap> primary_selection_; |
| 446 |
| 447 // Our X11 state. |
| 448 Display* x_display_; |
| 449 ::Window x_root_window_; |
| 450 |
| 451 // Input-only window used as a selection owner. |
| 452 ::Window x_window_; |
| 453 |
| 454 // True if we're currently running a nested message loop, waiting for data to |
| 455 // come back from the X server. |
| 456 bool in_nested_loop_; |
| 457 |
| 458 // Data to the current XConvertSelection request. Used for error detection; |
| 459 // we verify it on the return message. |
| 460 ::Atom current_selection_; |
| 461 ::Atom current_target_; |
| 462 |
| 463 // The property in the returning SelectNotify message is used to signal |
| 464 // success. If None, our request failed somehow. If equal to the property |
| 465 // atom that we sent in the XConvertSelection call, we can read that property |
| 466 // on |x_window_| for the requested data. |
| 467 ::Atom returned_property_; |
| 468 |
| 469 // Called to terminate the nested message loop. |
| 470 base::Closure quit_closure_; |
| 471 |
| 472 X11AtomCache atom_cache_; |
| 473 |
| 474 DISALLOW_COPY_AND_ASSIGN(AuraX11Details); |
| 475 }; |
| 476 |
| 477 Clipboard::AuraX11Details::AuraX11Details() |
| 478 : x_display_(GetXDisplay()), |
| 479 x_root_window_(DefaultRootWindow(x_display_)), |
| 480 in_nested_loop_(false), |
| 481 atom_cache_(x_display_, kAtomsToCache) { |
| 482 // We don't know all possible MIME types at compile time. |
| 483 atom_cache_.allow_uncached_atoms(); |
| 484 |
| 485 x_window_ = XCreateWindow( |
| 486 x_display_, x_root_window_, |
| 487 -100, -100, 10, 10, // x, y, width, height |
| 488 0, // border width |
| 489 CopyFromParent, // depth |
| 490 InputOnly, |
| 491 CopyFromParent, // visual |
| 492 0, |
| 493 NULL); |
| 494 XStoreName(x_display_, x_window_, "Chromium clipboard"); |
| 495 XSelectInput(x_display_, x_window_, PropertyChangeMask); |
| 496 |
| 497 base::MessagePumpAuraX11::Current()->AddDispatcherForWindow(this, x_window_); |
| 498 } |
| 499 |
| 500 Clipboard::AuraX11Details::~AuraX11Details() { |
| 501 base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(x_window_); |
| 502 |
| 503 XDestroyWindow(x_display_, x_window_); |
| 504 } |
| 505 |
| 506 ::Atom Clipboard::AuraX11Details::LookupSelectionForBuffer( |
| 507 Buffer buffer) const { |
| 508 if (buffer == BUFFER_STANDARD) |
| 509 return atom_cache_.GetAtom(kClipboard); |
| 510 else |
| 511 return XA_PRIMARY; |
| 512 } |
| 513 |
| 514 FormatMap* Clipboard::AuraX11Details::LookupStorageForAtom(::Atom atom) { |
| 515 if (atom == XA_PRIMARY) |
| 516 return primary_selection_.get(); |
| 517 else if (atom == atom_cache_.GetAtom(kClipboard)) |
| 518 return clipboard_selection_.get(); |
| 519 else |
| 520 return NULL; |
| 521 } |
| 522 |
| 523 void Clipboard::AuraX11Details::CreateNewClipboardData() { |
| 524 clipboard_data_.reset(new FormatMap); |
| 525 } |
| 526 |
| 527 void Clipboard::AuraX11Details::InsertMapping(const std::string& key, |
| 528 char* data, |
| 529 size_t data_len) { |
| 530 ::Atom atom_key = atom_cache_.GetAtom(key.c_str()); |
| 531 clipboard_data_->Insert(atom_key, data, data_len); |
| 532 } |
| 533 |
| 534 void Clipboard::AuraX11Details::TakeOwnershipOfSelection(Buffer buffer) { |
| 535 // Tell the X server that we are now the selection owner. |
| 536 ::Atom xselection = LookupSelectionForBuffer(buffer); |
| 537 XSetSelectionOwner(x_display_, xselection, x_window_, CurrentTime); |
| 538 |
| 539 if (XGetSelectionOwner(x_display_, xselection) == x_window_) { |
| 540 // The X server agrees that we are the selection owner. Commit our data. |
| 541 if (buffer == BUFFER_STANDARD) |
| 542 clipboard_selection_ = clipboard_data_.Pass(); |
| 543 else |
| 544 primary_selection_ = clipboard_data_.Pass(); |
| 545 } |
| 546 } |
| 547 |
| 548 scoped_ptr<SelectionData> Clipboard::AuraX11Details::RequestAndWaitForTypes( |
| 549 Buffer buffer, |
| 550 const std::vector< ::Atom>& types) { |
| 551 ::Atom selection_name = LookupSelectionForBuffer(buffer); |
| 552 if (XGetSelectionOwner(x_display_, selection_name) == x_window_) { |
| 553 // We can local fastpath instead of playing the nested message loop game |
| 554 // with the X server. |
| 555 FormatMap* format_map = LookupStorageForAtom(selection_name); |
| 556 DCHECK(format_map); |
| 557 |
| 558 for (std::vector< ::Atom>::const_iterator it = types.begin(); |
| 559 it != types.end(); ++it) { |
| 560 FormatMap::const_iterator format_map_it = format_map->find(*it); |
| 561 if (format_map_it != format_map->end()) { |
| 562 scoped_ptr<SelectionData> data_out(new SelectionData(&atom_cache_)); |
| 563 data_out->Set(format_map_it->first, format_map_it->second.first, |
| 564 format_map_it->second.second, false); |
| 565 return data_out.Pass(); |
| 566 } |
| 567 } |
| 568 } else { |
| 569 TargetList targets = WaitAndGetTargetsList(buffer); |
| 570 |
| 571 for (std::vector< ::Atom>::const_iterator it = types.begin(); |
| 572 it != types.end(); ++it) { |
| 573 unsigned char* data = NULL; |
| 574 size_t data_bytes = 0; |
| 575 ::Atom type = None; |
| 576 if (targets.ContainsAtom(*it) && |
| 577 PerformBlockingConvertSelection(selection_name, |
| 578 *it, |
| 579 &data, |
| 580 &data_bytes, |
| 581 NULL, |
| 582 &type) && |
| 583 type == *it) { |
| 584 scoped_ptr<SelectionData> data_out(new SelectionData(&atom_cache_)); |
| 585 data_out->Set(type, (char*)data, data_bytes, true); |
| 586 return data_out.Pass(); |
| 587 } |
| 588 } |
| 589 } |
| 590 |
| 591 return scoped_ptr<SelectionData>(); |
| 592 } |
| 593 |
| 594 TargetList Clipboard::AuraX11Details::WaitAndGetTargetsList( |
| 595 Buffer buffer) { |
| 596 ::Atom selection_name = LookupSelectionForBuffer(buffer); |
| 597 std::vector< ::Atom> out; |
| 598 if (XGetSelectionOwner(x_display_, selection_name) == x_window_) { |
| 599 // We can local fastpath and return the list of local targets. |
| 600 FormatMap* format_map = LookupStorageForAtom(selection_name); |
| 601 DCHECK(format_map); |
| 602 |
| 603 for (FormatMap::const_iterator it = format_map->begin(); |
| 604 it != format_map->end(); ++it) { |
| 605 out.push_back(it->first); |
| 606 } |
| 607 } else { |
| 608 unsigned char* data = NULL; |
| 609 size_t out_data_items = 0; |
| 610 ::Atom out_type = None; |
| 611 |
| 612 if (PerformBlockingConvertSelection(selection_name, |
| 613 atom_cache_.GetAtom(kTargets), |
| 614 &data, |
| 615 NULL, |
| 616 &out_data_items, |
| 617 &out_type)) { |
| 618 ::Atom* atom_array = reinterpret_cast< ::Atom*>(data); |
| 619 for (size_t i = 0; i < out_data_items; ++i) |
| 620 out.push_back(atom_array[i]); |
| 621 |
| 622 XFree(data); |
| 623 } else { |
| 624 // There was no target list. Most Java apps doesn't offer a TARGETS list, |
| 625 // even though they AWT to. They will offer individual text types if you |
| 626 // ask. If this is the case we attempt to make sense of the contents as |
| 627 // text. This is pretty unfortunate since it means we have to actually |
| 628 // copy the data to see if it is available, but at least this path |
| 629 // shouldn't be hit for conforming programs. |
| 630 std::vector< ::Atom> types = GetTextAtoms(); |
| 631 for (std::vector< ::Atom>::const_iterator it = types.begin(); |
| 632 it != types.end(); ++it) { |
| 633 ::Atom type = None; |
| 634 if (PerformBlockingConvertSelection(selection_name, |
| 635 *it, |
| 636 NULL, |
| 637 NULL, |
| 638 NULL, |
| 639 &type) && |
| 640 type == *it) { |
| 641 out.push_back(*it); |
| 642 } |
| 643 } |
| 644 } |
| 645 } |
| 646 |
| 647 return TargetList(out, &atom_cache_); |
| 648 } |
| 649 |
| 650 bool Clipboard::AuraX11Details::PerformBlockingConvertSelection( |
| 651 ::Atom selection_name, |
| 652 ::Atom target, |
| 653 unsigned char** out_data, |
| 654 size_t* out_data_bytes, |
| 655 size_t* out_data_items, |
| 656 ::Atom* out_type) { |
| 657 // The name of the property we're asking to be set on |x_window_|. |
| 658 ::Atom property_to_set = atom_cache_.GetAtom(kChromeSelection); |
| 659 |
| 660 XConvertSelection(x_display_, |
| 661 selection_name, |
| 662 target, |
| 663 property_to_set, |
| 664 x_window_, |
| 665 CurrentTime); |
| 666 |
| 667 // Now that we've thrown our message off to the X11 server, we block waiting |
| 668 // for a response. |
| 669 MessageLoopForUI* loop = MessageLoopForUI::current(); |
| 670 MessageLoop::ScopedNestableTaskAllower allow_nested(loop); |
| 671 base::RunLoop run_loop(base::MessagePumpAuraX11::Current()); |
| 672 |
| 673 current_selection_ = selection_name; |
| 674 current_target_ = target; |
| 675 in_nested_loop_ = true; |
| 676 quit_closure_ = run_loop.QuitClosure(); |
| 677 run_loop.Run(); |
| 678 in_nested_loop_ = false; |
| 679 current_selection_ = None; |
| 680 current_target_ = None; |
| 681 |
| 682 if (returned_property_ != property_to_set) |
| 683 return false; |
| 684 |
| 685 // Retrieve the data from our window. |
| 686 unsigned long nitems = 0; |
| 687 unsigned long nbytes = 0; |
| 688 Atom prop_type = None; |
| 689 int prop_format = 0; |
| 690 unsigned char* property_data = NULL; |
| 691 if (XGetWindowProperty(x_display_, |
| 692 x_window_, |
| 693 returned_property_, |
| 694 0, 0x1FFFFFFF /* MAXINT32 / 4 */, False, |
| 695 AnyPropertyType, &prop_type, &prop_format, |
| 696 &nitems, &nbytes, &property_data) != Success) { |
| 697 return false; |
| 698 } |
| 699 |
| 700 if (prop_type == None) |
| 701 return false; |
| 702 |
| 703 if (out_data) |
| 704 *out_data = property_data; |
| 705 |
| 706 if (out_data_bytes) { |
| 707 // So even though we should theoretically have nbytes (and we can't |
| 708 // pass NULL there), we need to manually calculate the byte length here |
| 709 // because nbytes always returns zero. |
| 710 switch (prop_format) { |
| 711 case 8: |
| 712 *out_data_bytes = nitems; |
| 713 break; |
| 714 case 16: |
| 715 *out_data_bytes = sizeof(short) * nitems; |
| 716 break; |
| 717 case 32: |
| 718 *out_data_bytes = sizeof(long) * nitems; |
| 719 break; |
| 720 default: |
| 721 NOTREACHED(); |
| 722 break; |
| 723 } |
| 724 } |
| 725 |
| 726 if (out_data_items) |
| 727 *out_data_items = nitems; |
| 728 |
| 729 if (out_type) |
| 730 *out_type = prop_type; |
| 731 |
| 732 return true; |
| 733 } |
| 734 |
| 735 std::vector< ::Atom> Clipboard::AuraX11Details::GetTextAtoms() const { |
| 736 return GetTextAtomsFrom(&atom_cache_); |
| 737 } |
| 738 |
| 739 std::vector< ::Atom> Clipboard::AuraX11Details::GetAtomsForFormat( |
| 740 const Clipboard::FormatType& format) { |
| 741 std::vector< ::Atom> atoms; |
| 742 atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str())); |
| 743 return atoms; |
| 744 } |
| 745 |
| 746 void Clipboard::AuraX11Details::Clear(Buffer buffer) { |
| 747 ::Atom selection_name = LookupSelectionForBuffer(buffer); |
| 748 if (XGetSelectionOwner(x_display_, selection_name) == x_window_) |
| 749 XSetSelectionOwner(x_display_, selection_name, None, CurrentTime); |
| 750 |
| 751 if (buffer == BUFFER_STANDARD) |
| 752 clipboard_selection_.reset(); |
| 753 else |
| 754 primary_selection_.reset(); |
| 755 } |
| 756 |
| 757 void Clipboard::AuraX11Details::HandleSelectionRequest( |
| 758 const XSelectionRequestEvent& event) { |
| 759 // Incrementally build our selection. By default this is a refusal, and we'll |
| 760 // override the parts indicating success in the different cases. |
| 761 XEvent reply; |
| 762 reply.xselection.type = SelectionNotify; |
| 763 reply.xselection.requestor = event.requestor; |
| 764 reply.xselection.selection = event.selection; |
| 765 reply.xselection.target = event.target; |
| 766 reply.xselection.property = None; // Indicates failure |
| 767 reply.xselection.time = event.time; |
| 768 |
| 769 // Get the proper selection. |
| 770 FormatMap* format_map = LookupStorageForAtom(event.selection); |
| 771 if (format_map) { |
| 772 ::Atom targets_atom = atom_cache_.GetAtom(kTargets); |
| 773 if (event.target == targets_atom) { |
| 774 std::vector< ::Atom> targets; |
| 775 targets.push_back(targets_atom); |
| 776 for (FormatMap::const_iterator it = format_map->begin(); |
| 777 it != format_map->end(); ++it) { |
| 778 targets.push_back(it->first); |
| 779 } |
| 780 |
| 781 XChangeProperty(x_display_, event.requestor, event.property, XA_ATOM, 32, |
| 782 PropModeReplace, |
| 783 reinterpret_cast<unsigned char*>(&targets.front()), |
| 784 targets.size()); |
| 785 reply.xselection.property = event.property; |
| 786 } else if (event.target == atom_cache_.GetAtom(kMultiple)) { |
| 787 // TODO(erg): Theoretically, the spec claims I'm supposed to handle the |
| 788 // MULTIPLE case, but I haven't seen it in the wild yet. |
| 789 NOTIMPLEMENTED(); |
| 790 } else { |
| 791 // Try to find the data type in map. |
| 792 FormatMap::const_iterator it = format_map->find(event.target); |
| 793 if (it != format_map->end()) { |
| 794 XChangeProperty(x_display_, event.requestor, event.property, |
| 795 event.target, 8, |
| 796 PropModeReplace, |
| 797 reinterpret_cast<unsigned char*>(it->second.first), |
| 798 it->second.second); |
| 799 reply.xselection.property = event.property; |
| 800 } |
| 801 // I would put error logging here, but GTK ignores TARGETS and spams us |
| 802 // looking for its own internal types. |
| 803 } |
| 804 } else { |
| 805 DLOG(ERROR) << "Requested on a selection we don't support: " |
| 806 << XGetAtomName(x_display_, event.selection); |
| 807 } |
| 808 |
| 809 // Send off the reply. |
| 810 XSendEvent(x_display_, event.requestor, False, 0, &reply); |
| 811 } |
| 812 |
| 813 void Clipboard::AuraX11Details::HandleSelectionNotify( |
| 814 const XSelectionEvent& event) { |
| 815 if (!in_nested_loop_) { |
| 816 // This shouldn't happen; we're not waiting on the X server for data, but |
| 817 // any client can send any message... |
| 818 return; |
| 819 } |
| 820 |
| 821 if (current_selection_ == event.selection && |
| 822 current_target_ == event.target) { |
| 823 returned_property_ = event.property; |
| 824 } else { |
| 825 // I am assuming that if some other client sent us a message after we've |
| 826 // asked for data, but it's malformed, we should just treat as if they sent |
| 827 // us an error message. |
| 828 returned_property_ = None; |
| 829 } |
| 830 |
| 831 quit_closure_.Run(); |
| 832 } |
| 833 |
| 834 void Clipboard::AuraX11Details::HandleSelectionClear( |
| 835 const XSelectionClearEvent& event) { |
| 836 DLOG(ERROR) << "SelectionClear"; |
| 837 |
| 838 // TODO(erg): If we receive a SelectionClear event while we're handling data, |
| 839 // we need to delay clearing. |
| 840 } |
| 841 |
| 842 void Clipboard::AuraX11Details::HandlePropertyNotify( |
| 843 const XPropertyEvent& event) { |
| 844 // TODO(erg): Must handle PropertyNotify events on our |x_window_| as part |
| 845 // of receiving data during paste. |
| 846 } |
| 847 |
| 848 bool Clipboard::AuraX11Details::Dispatch(const base::NativeEvent& event) { |
| 849 XEvent* xev = event; |
| 850 |
| 851 switch (xev->type) { |
| 852 case SelectionRequest: |
| 853 HandleSelectionRequest(xev->xselectionrequest); |
| 854 break; |
| 855 case SelectionNotify: |
| 856 HandleSelectionNotify(xev->xselection); |
| 857 break; |
| 858 case SelectionClear: |
| 859 HandleSelectionClear(xev->xselectionclear); |
| 860 break; |
| 861 case PropertyNotify: |
| 862 HandlePropertyNotify(xev->xproperty); |
| 863 break; |
| 864 default: |
| 865 break; |
| 866 } |
| 867 |
| 868 return true; |
| 869 } |
| 870 |
| 871 /////////////////////////////////////////////////////////////////////////////// |
| 872 // Clipboard |
| 873 |
| 874 Clipboard::Clipboard() |
| 875 : aurax11_details_(new AuraX11Details) { |
| 440 DCHECK(CalledOnValidThread()); | 876 DCHECK(CalledOnValidThread()); |
| 441 // Make sure clipboard is created. | |
| 442 GetClipboard(); | |
| 443 } | 877 } |
| 444 | 878 |
| 445 Clipboard::~Clipboard() { | 879 Clipboard::~Clipboard() { |
| 446 DCHECK(CalledOnValidThread()); | 880 DCHECK(CalledOnValidThread()); |
| 447 DeleteClipboard(); | 881 |
| 882 // TODO(erg): We need to do whatever the equivalent of |
| 883 // gtk_clipboard_store(clipboard_) is here. When we shut down, we want the |
| 884 // current selection to live on. |
| 448 } | 885 } |
| 449 | 886 |
| 450 void Clipboard::WriteObjects(Buffer buffer, const ObjectMap& objects) { | 887 void Clipboard::WriteObjects(Buffer buffer, const ObjectMap& objects) { |
| 451 DCHECK(CalledOnValidThread()); | 888 DCHECK(CalledOnValidThread()); |
| 452 DCHECK(IsValidBuffer(buffer)); | 889 DCHECK(IsValidBuffer(buffer)); |
| 890 |
| 891 aurax11_details_->CreateNewClipboardData(); |
| 453 for (ObjectMap::const_iterator iter = objects.begin(); | 892 for (ObjectMap::const_iterator iter = objects.begin(); |
| 454 iter != objects.end(); ++iter) { | 893 iter != objects.end(); ++iter) { |
| 455 DispatchObject(static_cast<ObjectType>(iter->first), iter->second); | 894 DispatchObject(static_cast<ObjectType>(iter->first), iter->second); |
| 456 } | 895 } |
| 457 ClipboardDataBuilder::CommitToClipboard(); | 896 |
| 897 aurax11_details_->TakeOwnershipOfSelection(buffer); |
| 458 } | 898 } |
| 459 | 899 |
| 460 bool Clipboard::IsFormatAvailable(const FormatType& format, | 900 bool Clipboard::IsFormatAvailable(const FormatType& format, |
| 461 Buffer buffer) const { | 901 Buffer buffer) const { |
| 462 DCHECK(CalledOnValidThread()); | 902 DCHECK(CalledOnValidThread()); |
| 463 DCHECK(IsValidBuffer(buffer)); | 903 DCHECK(IsValidBuffer(buffer)); |
| 464 AuraClipboard* clipboard = GetClipboard(); | 904 |
| 465 if (GetPlainTextFormatType().Equals(format) || | 905 TargetList target_list = aurax11_details_->WaitAndGetTargetsList(buffer); |
| 466 GetUrlFormatType().Equals(format)) | 906 return target_list.ContainsFormat(format); |
| 467 return clipboard->IsFormatAvailable(TEXT); | |
| 468 else if (GetHtmlFormatType().Equals(format)) | |
| 469 return clipboard->IsFormatAvailable(HTML); | |
| 470 else if (GetRtfFormatType().Equals(format)) | |
| 471 return clipboard->IsFormatAvailable(RTF); | |
| 472 else if (GetBitmapFormatType().Equals(format)) | |
| 473 return clipboard->IsFormatAvailable(BITMAP); | |
| 474 else if (GetWebKitSmartPasteFormatType().Equals(format)) | |
| 475 return clipboard->IsFormatAvailable(WEB); | |
| 476 else { | |
| 477 const ClipboardData* data = clipboard->GetData(); | |
| 478 if (data && data->custom_data_format() == format.ToString()) | |
| 479 return true; | |
| 480 } | |
| 481 return false; | |
| 482 } | 907 } |
| 483 | 908 |
| 484 void Clipboard::Clear(Buffer buffer) { | 909 void Clipboard::Clear(Buffer buffer) { |
| 485 DCHECK(CalledOnValidThread()); | 910 DCHECK(CalledOnValidThread()); |
| 486 DCHECK(IsValidBuffer(buffer)); | 911 DCHECK(IsValidBuffer(buffer)); |
| 487 AuraClipboard* clipboard = GetClipboard(); | 912 aurax11_details_->Clear(buffer); |
| 488 clipboard->Clear(); | |
| 489 } | 913 } |
| 490 | 914 |
| 491 void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types, | 915 void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types, |
| 492 bool* contains_filenames) const { | 916 bool* contains_filenames) const { |
| 493 DCHECK(CalledOnValidThread()); | 917 DCHECK(CalledOnValidThread()); |
| 494 if (!types || !contains_filenames) { | 918 if (!types || !contains_filenames) { |
| 495 NOTREACHED(); | 919 NOTREACHED(); |
| 496 return; | 920 return; |
| 497 } | 921 } |
| 498 | 922 |
| 923 TargetList target_list = aurax11_details_->WaitAndGetTargetsList(buffer); |
| 924 |
| 499 types->clear(); | 925 types->clear(); |
| 926 |
| 927 if (target_list.ContainsText()) |
| 928 types->push_back(UTF8ToUTF16(kMimeTypeText)); |
| 929 if (target_list.ContainsFormat(GetHtmlFormatType())) |
| 930 types->push_back(UTF8ToUTF16(kMimeTypeHTML)); |
| 931 if (target_list.ContainsFormat(GetRtfFormatType())) |
| 932 types->push_back(UTF8ToUTF16(kMimeTypeRTF)); |
| 933 if (target_list.ContainsFormat(GetBitmapFormatType())) |
| 934 types->push_back(UTF8ToUTF16(kMimeTypePNG)); |
| 500 *contains_filenames = false; | 935 *contains_filenames = false; |
| 501 if (IsFormatAvailable(GetPlainTextFormatType(), buffer)) | 936 |
| 502 types->push_back(UTF8ToUTF16(GetPlainTextFormatType().ToString())); | 937 scoped_ptr<SelectionData> data(aurax11_details_->RequestAndWaitForTypes( |
| 503 if (IsFormatAvailable(GetHtmlFormatType(), buffer)) | 938 buffer, |
| 504 types->push_back(UTF8ToUTF16(GetHtmlFormatType().ToString())); | 939 aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType()))); |
| 505 if (IsFormatAvailable(GetRtfFormatType(), buffer)) | 940 if (!data.get()) |
| 506 types->push_back(UTF8ToUTF16(GetRtfFormatType().ToString())); | 941 return; |
| 507 if (IsFormatAvailable(GetBitmapFormatType(), buffer)) | 942 |
| 508 types->push_back(UTF8ToUTF16(GetBitmapFormatType().ToString())); | 943 ReadCustomDataTypes(data->data(), data->size(), types); |
| 509 | 944 } |
| 510 AuraClipboard* clipboard = GetClipboard(); | 945 |
| 511 if (clipboard->IsFormatAvailable(CUSTOM) && clipboard->GetData()) { | 946 void Clipboard::ReadText(Buffer buffer, string16* result) const { |
| 512 ui::ReadCustomDataTypes(clipboard->GetData()->custom_data_data().c_str(), | 947 DCHECK(CalledOnValidThread()); |
| 513 clipboard->GetData()->custom_data_data().size(), types); | 948 |
| 949 scoped_ptr<SelectionData> data(aurax11_details_->RequestAndWaitForTypes( |
| 950 buffer, aurax11_details_->GetTextAtoms())); |
| 951 if (data.get()) { |
| 952 std::string text = data->GetText(); |
| 953 *result = UTF8ToUTF16(text); |
| 514 } | 954 } |
| 515 } | 955 } |
| 516 | 956 |
| 517 void Clipboard::ReadText(Buffer buffer, string16* result) const { | |
| 518 DCHECK(CalledOnValidThread()); | |
| 519 GetClipboard()->ReadText(result); | |
| 520 } | |
| 521 | |
| 522 void Clipboard::ReadAsciiText(Buffer buffer, std::string* result) const { | 957 void Clipboard::ReadAsciiText(Buffer buffer, std::string* result) const { |
| 523 DCHECK(CalledOnValidThread()); | 958 DCHECK(CalledOnValidThread()); |
| 524 GetClipboard()->ReadAsciiText(result); | 959 |
| 525 } | 960 scoped_ptr<SelectionData> data(aurax11_details_->RequestAndWaitForTypes( |
| 526 | 961 buffer, aurax11_details_->GetTextAtoms())); |
| 962 if (data.get()) |
| 963 result->assign(data->GetText()); |
| 964 } |
| 965 |
| 966 // TODO(estade): handle different charsets. |
| 967 // TODO(port): set *src_url. |
| 527 void Clipboard::ReadHTML(Buffer buffer, | 968 void Clipboard::ReadHTML(Buffer buffer, |
| 528 string16* markup, | 969 string16* markup, |
| 529 std::string* src_url, | 970 std::string* src_url, |
| 530 uint32* fragment_start, | 971 uint32* fragment_start, |
| 531 uint32* fragment_end) const { | 972 uint32* fragment_end) const { |
| 532 DCHECK(CalledOnValidThread()); | 973 DCHECK(CalledOnValidThread()); |
| 533 GetClipboard()->ReadHTML(markup, src_url, fragment_start, fragment_end); | 974 markup->clear(); |
| 975 if (src_url) |
| 976 src_url->clear(); |
| 977 *fragment_start = 0; |
| 978 *fragment_end = 0; |
| 979 |
| 980 scoped_ptr<SelectionData> data(aurax11_details_->RequestAndWaitForTypes( |
| 981 buffer, aurax11_details_->GetAtomsForFormat(GetHtmlFormatType()))); |
| 982 if (!data.get()) |
| 983 return; |
| 984 |
| 985 // If the data starts with 0xFEFF, i.e., Byte Order Mark, assume it is |
| 986 // UTF-16, otherwise assume UTF-8. |
| 987 if (data->size() >= 2 && |
| 988 reinterpret_cast<const uint16_t*>(data->data())[0] == 0xFEFF) { |
| 989 markup->assign(reinterpret_cast<const uint16_t*>(data->data()) + 1, |
| 990 (data->size() / 2) - 1); |
| 991 } else { |
| 992 UTF8ToUTF16(reinterpret_cast<const char*>(data->data()), data->size(), |
| 993 markup); |
| 994 } |
| 995 |
| 996 // If there is a terminating NULL, drop it. |
| 997 if (!markup->empty() && markup->at(markup->length() - 1) == '\0') |
| 998 markup->resize(markup->length() - 1); |
| 999 |
| 1000 *fragment_start = 0; |
| 1001 DCHECK(markup->length() <= kuint32max); |
| 1002 *fragment_end = static_cast<uint32>(markup->length()); |
| 534 } | 1003 } |
| 535 | 1004 |
| 536 void Clipboard::ReadRTF(Buffer buffer, std::string* result) const { | 1005 void Clipboard::ReadRTF(Buffer buffer, std::string* result) const { |
| 537 DCHECK(CalledOnValidThread()); | 1006 DCHECK(CalledOnValidThread()); |
| 538 GetClipboard()->ReadRTF(result); | 1007 |
| 1008 scoped_ptr<SelectionData> data(aurax11_details_->RequestAndWaitForTypes( |
| 1009 buffer, aurax11_details_->GetAtomsForFormat(GetRtfFormatType()))); |
| 1010 if (data.get()) |
| 1011 data->AssignTo(result); |
| 539 } | 1012 } |
| 540 | 1013 |
| 541 SkBitmap Clipboard::ReadImage(Buffer buffer) const { | 1014 SkBitmap Clipboard::ReadImage(Buffer buffer) const { |
| 542 DCHECK(CalledOnValidThread()); | 1015 DCHECK(CalledOnValidThread()); |
| 543 return GetClipboard()->ReadImage(); | 1016 NOTIMPLEMENTED(); |
| 1017 return SkBitmap(); |
| 544 } | 1018 } |
| 545 | 1019 |
| 546 void Clipboard::ReadCustomData(Buffer buffer, | 1020 void Clipboard::ReadCustomData(Buffer buffer, |
| 547 const string16& type, | 1021 const string16& type, |
| 548 string16* result) const { | 1022 string16* result) const { |
| 549 DCHECK(CalledOnValidThread()); | 1023 DCHECK(CalledOnValidThread()); |
| 550 GetClipboard()->ReadCustomData(type, result); | 1024 |
| 1025 scoped_ptr<SelectionData> data(aurax11_details_->RequestAndWaitForTypes( |
| 1026 buffer, |
| 1027 aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType()))); |
| 1028 if (!data.get()) |
| 1029 return; |
| 1030 |
| 1031 ReadCustomDataForType(data->data(), data->size(), type, result); |
| 551 } | 1032 } |
| 552 | 1033 |
| 553 void Clipboard::ReadBookmark(string16* title, std::string* url) const { | 1034 void Clipboard::ReadBookmark(string16* title, std::string* url) const { |
| 554 DCHECK(CalledOnValidThread()); | 1035 DCHECK(CalledOnValidThread()); |
| 555 GetClipboard()->ReadBookmark(title, url); | 1036 // TODO(erg): This was left NOTIMPLEMENTED() in the gtk port too. |
| 1037 NOTIMPLEMENTED(); |
| 556 } | 1038 } |
| 557 | 1039 |
| 558 void Clipboard::ReadData(const FormatType& format, std::string* result) const { | 1040 void Clipboard::ReadData(const FormatType& format, std::string* result) const { |
| 559 DCHECK(CalledOnValidThread()); | 1041 DCHECK(CalledOnValidThread()); |
| 560 GetClipboard()->ReadData(format.ToString(), result); | 1042 |
| 1043 scoped_ptr<SelectionData> data(aurax11_details_->RequestAndWaitForTypes( |
| 1044 BUFFER_STANDARD, aurax11_details_->GetAtomsForFormat(format))); |
| 1045 if (data.get()) |
| 1046 data->AssignTo(result); |
| 1047 } |
| 1048 |
| 1049 // When a URL is copied from a render view context menu (via "copy link |
| 1050 // location", for example), we additionally stick it in the X clipboard. This |
| 1051 // matches other linux browsers. |
| 1052 void Clipboard::DidWriteURL(const std::string& utf8_text) { |
| 1053 DCHECK(CalledOnValidThread()); |
| 1054 |
| 1055 aurax11_details_->CreateNewClipboardData(); |
| 1056 WriteText(utf8_text.c_str(), utf8_text.size()); |
| 1057 aurax11_details_->TakeOwnershipOfSelection(BUFFER_SELECTION); |
| 561 } | 1058 } |
| 562 | 1059 |
| 563 uint64 Clipboard::GetSequenceNumber(Buffer buffer) { | 1060 uint64 Clipboard::GetSequenceNumber(Buffer buffer) { |
| 564 DCHECK(CalledOnValidThread()); | 1061 DCHECK(CalledOnValidThread()); |
| 565 return GetClipboard()->GetNumClipboardEntries(); | 1062 if (buffer == BUFFER_STANDARD) |
| 1063 return SelectionChangeObserver::GetInstance()->clipboard_sequence_number(); |
| 1064 else |
| 1065 return SelectionChangeObserver::GetInstance()->primary_sequence_number(); |
| 566 } | 1066 } |
| 567 | 1067 |
| 568 void Clipboard::WriteText(const char* text_data, size_t text_len) { | 1068 void Clipboard::WriteText(const char* text_data, size_t text_len) { |
| 569 ClipboardDataBuilder::WriteText(text_data, text_len); | 1069 char* data = new char[text_len]; |
| 1070 memcpy(data, text_data, text_len); |
| 1071 |
| 1072 aurax11_details_->InsertMapping(kMimeTypeText, data, text_len); |
| 1073 aurax11_details_->InsertMapping(kText, data, text_len); |
| 1074 aurax11_details_->InsertMapping(kString, data, text_len); |
| 1075 aurax11_details_->InsertMapping(kUtf8String, data, text_len); |
| 570 } | 1076 } |
| 571 | 1077 |
| 572 void Clipboard::WriteHTML(const char* markup_data, | 1078 void Clipboard::WriteHTML(const char* markup_data, |
| 573 size_t markup_len, | 1079 size_t markup_len, |
| 574 const char* url_data, | 1080 const char* url_data, |
| 575 size_t url_len) { | 1081 size_t url_len) { |
| 576 ClipboardDataBuilder::WriteHTML(markup_data, markup_len, url_data, url_len); | 1082 // TODO(estade): We need to expand relative links with |url_data|. |
| 1083 static const char* html_prefix = "<meta http-equiv=\"content-type\" " |
| 1084 "content=\"text/html; charset=utf-8\">"; |
| 1085 size_t html_prefix_len = strlen(html_prefix); |
| 1086 size_t total_len = html_prefix_len + markup_len + 1; |
| 1087 |
| 1088 char* data = new char[total_len]; |
| 1089 snprintf(data, total_len, "%s", html_prefix); |
| 1090 memcpy(data + html_prefix_len, markup_data, markup_len); |
| 1091 // Some programs expect NULL-terminated data. See http://crbug.com/42624 |
| 1092 data[total_len - 1] = '\0'; |
| 1093 |
| 1094 aurax11_details_->InsertMapping(kMimeTypeHTML, data, total_len); |
| 577 } | 1095 } |
| 578 | 1096 |
| 579 void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) { | 1097 void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) { |
| 580 ClipboardDataBuilder::WriteRTF(rtf_data, data_len); | 1098 WriteData(GetRtfFormatType(), rtf_data, data_len); |
| 581 } | 1099 } |
| 582 | 1100 |
| 583 void Clipboard::WriteBookmark(const char* title_data, | 1101 void Clipboard::WriteBookmark(const char* title_data, |
| 584 size_t title_len, | 1102 size_t title_len, |
| 585 const char* url_data, | 1103 const char* url_data, |
| 586 size_t url_len) { | 1104 size_t url_len) { |
| 587 ClipboardDataBuilder::WriteBookmark(title_data, title_len, url_data, url_len); | 1105 // Write as a mozilla url (UTF16: URL, newline, title). |
| 588 } | 1106 string16 url = UTF8ToUTF16(std::string(url_data, url_len) + "\n"); |
| 589 | 1107 string16 title = UTF8ToUTF16(std::string(title_data, title_len)); |
| 1108 int data_len = 2 * (title.length() + url.length()); |
| 1109 |
| 1110 char* data = new char[data_len]; |
| 1111 memcpy(data, url.data(), 2 * url.length()); |
| 1112 memcpy(data + 2 * url.length(), title.data(), 2 * title.length()); |
| 1113 aurax11_details_->InsertMapping(kMimeTypeMozillaURL, data, data_len); |
| 1114 } |
| 1115 |
| 1116 // Write an extra flavor that signifies WebKit was the last to modify the |
| 1117 // pasteboard. This flavor has no data. |
| 590 void Clipboard::WriteWebSmartPaste() { | 1118 void Clipboard::WriteWebSmartPaste() { |
| 591 ClipboardDataBuilder::WriteWebSmartPaste(); | 1119 aurax11_details_->InsertMapping(kMimeTypeWebkitSmartPaste, NULL, 0); |
| 592 } | 1120 } |
| 593 | 1121 |
| 594 void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) { | 1122 void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) { |
| 595 ClipboardDataBuilder::WriteBitmap(pixel_data, size_data); | 1123 // TODO(erg): I'm not sure if we should be writting BMP data here or |
| 1124 // not. It's what the GTK port does, but I'm not sure it's the right thing to |
| 1125 // do. |
| 1126 NOTIMPLEMENTED(); |
| 596 } | 1127 } |
| 597 | 1128 |
| 598 void Clipboard::WriteData(const FormatType& format, | 1129 void Clipboard::WriteData(const FormatType& format, |
| 599 const char* data_data, | 1130 const char* data_data, |
| 600 size_t data_len) { | 1131 size_t data_len) { |
| 601 ClipboardDataBuilder::WriteData(format.ToString(), data_data, data_len); | 1132 // We assume that certain mapping types are only written by trusted code. |
| 1133 // Therefore we must upkeep their integrity. |
| 1134 if (format.Equals(GetBitmapFormatType())) |
| 1135 return; |
| 1136 char* data = new char[data_len]; |
| 1137 memcpy(data, data_data, data_len); |
| 1138 aurax11_details_->InsertMapping(format.ToString(), data, data_len); |
| 602 } | 1139 } |
| 603 | 1140 |
| 604 // static | 1141 // static |
| 605 Clipboard::FormatType Clipboard::GetFormatType( | 1142 Clipboard::FormatType Clipboard::GetFormatType( |
| 606 const std::string& format_string) { | 1143 const std::string& format_string) { |
| 607 return FormatType::Deserialize(format_string); | 1144 return FormatType::Deserialize(format_string); |
| 608 } | 1145 } |
| 609 | 1146 |
| 610 // static | 1147 // static |
| 611 const Clipboard::FormatType& Clipboard::GetUrlFormatType() { | 1148 const Clipboard::FormatType& Clipboard::GetUrlFormatType() { |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 664 return type; | 1201 return type; |
| 665 } | 1202 } |
| 666 | 1203 |
| 667 // static | 1204 // static |
| 668 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { | 1205 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { |
| 669 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); | 1206 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); |
| 670 return type; | 1207 return type; |
| 671 } | 1208 } |
| 672 | 1209 |
| 673 } // namespace ui | 1210 } // namespace ui |
| OLD | NEW |