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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ui/base/clipboard/clipboard.h ('k') | ui/base/clipboard/clipboard_chromeos.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
OLDNEW
« 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