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

Side by Side Diff: ui/base/dragdrop/os_exchange_data_provider_aurax11.cc

Issue 851853002: It is time. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Trying to reup because the last upload failed. Created 5 years, 11 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
6
7 #include "base/logging.h"
8 #include "base/memory/ref_counted_memory.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "net/base/filename_util.h"
12 #include "ui/base/clipboard/clipboard.h"
13 #include "ui/base/clipboard/scoped_clipboard_writer.h"
14 #include "ui/base/dragdrop/file_info.h"
15 #include "ui/base/x/selection_utils.h"
16 #include "ui/base/x/x11_util.h"
17 #include "ui/events/platform/platform_event_source.h"
18
19 // Note: the GetBlah() methods are used immediately by the
20 // web_contents_view_aura.cc:PrepareDropData(), while the omnibox is a
21 // little more discriminating and calls HasBlah() before trying to get the
22 // information.
23
24 namespace ui {
25
26 namespace {
27
28 const char kDndSelection[] = "XdndSelection";
29 const char kRendererTaint[] = "chromium/x-renderer-taint";
30
31 const char kNetscapeURL[] = "_NETSCAPE_URL";
32
33 const char* kAtomsToCache[] = {
34 kString,
35 kText,
36 kUtf8String,
37 kDndSelection,
38 Clipboard::kMimeTypeURIList,
39 kMimeTypeMozillaURL,
40 kNetscapeURL,
41 Clipboard::kMimeTypeText,
42 kRendererTaint,
43 NULL
44 };
45
46 } // namespace
47
48 OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11(
49 ::Window x_window,
50 const SelectionFormatMap& selection)
51 : x_display_(gfx::GetXDisplay()),
52 x_root_window_(DefaultRootWindow(x_display_)),
53 own_window_(false),
54 x_window_(x_window),
55 atom_cache_(x_display_, kAtomsToCache),
56 format_map_(selection),
57 selection_owner_(x_display_, x_window_,
58 atom_cache_.GetAtom(kDndSelection)) {
59 // We don't know all possible MIME types at compile time.
60 atom_cache_.allow_uncached_atoms();
61 }
62
63 OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11()
64 : x_display_(gfx::GetXDisplay()),
65 x_root_window_(DefaultRootWindow(x_display_)),
66 own_window_(true),
67 x_window_(XCreateWindow(
68 x_display_,
69 x_root_window_,
70 -100, -100, 10, 10, // x, y, width, height
71 0, // border width
72 CopyFromParent, // depth
73 InputOnly,
74 CopyFromParent, // visual
75 0,
76 NULL)),
77 atom_cache_(x_display_, kAtomsToCache),
78 format_map_(),
79 selection_owner_(x_display_, x_window_,
80 atom_cache_.GetAtom(kDndSelection)) {
81 // We don't know all possible MIME types at compile time.
82 atom_cache_.allow_uncached_atoms();
83
84 XStoreName(x_display_, x_window_, "Chromium Drag & Drop Window");
85
86 PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
87 }
88
89 OSExchangeDataProviderAuraX11::~OSExchangeDataProviderAuraX11() {
90 if (own_window_) {
91 PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
92 XDestroyWindow(x_display_, x_window_);
93 }
94 }
95
96 void OSExchangeDataProviderAuraX11::TakeOwnershipOfSelection() const {
97 selection_owner_.TakeOwnershipOfSelection(format_map_);
98 }
99
100 void OSExchangeDataProviderAuraX11::RetrieveTargets(
101 std::vector<Atom>* targets) const {
102 selection_owner_.RetrieveTargets(targets);
103 }
104
105 SelectionFormatMap OSExchangeDataProviderAuraX11::GetFormatMap() const {
106 // We return the |selection_owner_|'s format map instead of our own in case
107 // ours has been modified since TakeOwnershipOfSelection() was called.
108 return selection_owner_.selection_format_map();
109 }
110
111 OSExchangeData::Provider* OSExchangeDataProviderAuraX11::Clone() const {
112 OSExchangeDataProviderAuraX11* ret = new OSExchangeDataProviderAuraX11();
113 ret->format_map_ = format_map_;
114 return ret;
115 }
116
117 void OSExchangeDataProviderAuraX11::MarkOriginatedFromRenderer() {
118 std::string empty;
119 format_map_.Insert(atom_cache_.GetAtom(kRendererTaint),
120 scoped_refptr<base::RefCountedMemory>(
121 base::RefCountedString::TakeString(&empty)));
122 }
123
124 bool OSExchangeDataProviderAuraX11::DidOriginateFromRenderer() const {
125 return format_map_.find(atom_cache_.GetAtom(kRendererTaint)) !=
126 format_map_.end();
127 }
128
129 void OSExchangeDataProviderAuraX11::SetString(const base::string16& text_data) {
130 if (HasString())
131 return;
132
133 std::string utf8 = base::UTF16ToUTF8(text_data);
134 scoped_refptr<base::RefCountedMemory> mem(
135 base::RefCountedString::TakeString(&utf8));
136
137 format_map_.Insert(atom_cache_.GetAtom(Clipboard::kMimeTypeText), mem);
138 format_map_.Insert(atom_cache_.GetAtom(kText), mem);
139 format_map_.Insert(atom_cache_.GetAtom(kString), mem);
140 format_map_.Insert(atom_cache_.GetAtom(kUtf8String), mem);
141 }
142
143 void OSExchangeDataProviderAuraX11::SetURL(const GURL& url,
144 const base::string16& title) {
145 // TODO(dcheng): The original GTK code tries very hard to avoid writing out an
146 // empty title. Is this necessary?
147 if (url.is_valid()) {
148 // Mozilla's URL format: (UTF16: URL, newline, title)
149 base::string16 spec = base::UTF8ToUTF16(url.spec());
150
151 std::vector<unsigned char> data;
152 ui::AddString16ToVector(spec, &data);
153 ui::AddString16ToVector(base::ASCIIToUTF16("\n"), &data);
154 ui::AddString16ToVector(title, &data);
155 scoped_refptr<base::RefCountedMemory> mem(
156 base::RefCountedBytes::TakeVector(&data));
157
158 format_map_.Insert(atom_cache_.GetAtom(kMimeTypeMozillaURL), mem);
159
160 // Set a string fallback as well.
161 SetString(spec);
162
163 // Return early if this drag already contains file contents (this implies
164 // that file contents must be populated before URLs). Nautilus (and possibly
165 // other file managers) prefer _NETSCAPE_URL over the X Direct Save
166 // protocol, but we want to prioritize XDS in this case.
167 if (!file_contents_name_.empty())
168 return;
169
170 // Set _NETSCAPE_URL for file managers like Nautilus that use it as a hint
171 // to create a link to the URL. Setting text/uri-list doesn't work because
172 // Nautilus will fetch and copy the contents of the URL to the drop target
173 // instead of linking...
174 // Format is UTF8: URL + "\n" + title.
175 std::string netscape_url = url.spec();
176 netscape_url += "\n";
177 netscape_url += base::UTF16ToUTF8(title);
178 format_map_.Insert(atom_cache_.GetAtom(kNetscapeURL),
179 scoped_refptr<base::RefCountedMemory>(
180 base::RefCountedString::TakeString(&netscape_url)));
181 }
182 }
183
184 void OSExchangeDataProviderAuraX11::SetFilename(const base::FilePath& path) {
185 std::vector<FileInfo> data;
186 data.push_back(FileInfo(path, base::FilePath()));
187 SetFilenames(data);
188 }
189
190 void OSExchangeDataProviderAuraX11::SetFilenames(
191 const std::vector<FileInfo>& filenames) {
192 std::vector<std::string> paths;
193 for (std::vector<FileInfo>::const_iterator it = filenames.begin();
194 it != filenames.end();
195 ++it) {
196 std::string url_spec = net::FilePathToFileURL(it->path).spec();
197 if (!url_spec.empty())
198 paths.push_back(url_spec);
199 }
200
201 std::string joined_data = JoinString(paths, '\n');
202 scoped_refptr<base::RefCountedMemory> mem(
203 base::RefCountedString::TakeString(&joined_data));
204 format_map_.Insert(atom_cache_.GetAtom(Clipboard::kMimeTypeURIList), mem);
205 }
206
207 void OSExchangeDataProviderAuraX11::SetPickledData(
208 const OSExchangeData::CustomFormat& format,
209 const Pickle& pickle) {
210 const unsigned char* data =
211 reinterpret_cast<const unsigned char*>(pickle.data());
212
213 std::vector<unsigned char> bytes;
214 bytes.insert(bytes.end(), data, data + pickle.size());
215 scoped_refptr<base::RefCountedMemory> mem(
216 base::RefCountedBytes::TakeVector(&bytes));
217
218 format_map_.Insert(atom_cache_.GetAtom(format.ToString().c_str()), mem);
219 }
220
221 bool OSExchangeDataProviderAuraX11::GetString(base::string16* result) const {
222 if (HasFile()) {
223 // Various Linux file managers both pass a list of file:// URIs and set the
224 // string representation to the URI. We explicitly don't want to return use
225 // this representation.
226 return false;
227 }
228
229 std::vector< ::Atom> text_atoms = ui::GetTextAtomsFrom(&atom_cache_);
230 std::vector< ::Atom> requested_types;
231 ui::GetAtomIntersection(text_atoms, GetTargets(), &requested_types);
232
233 ui::SelectionData data(format_map_.GetFirstOf(requested_types));
234 if (data.IsValid()) {
235 std::string text = data.GetText();
236 *result = base::UTF8ToUTF16(text);
237 return true;
238 }
239
240 return false;
241 }
242
243 bool OSExchangeDataProviderAuraX11::GetURLAndTitle(
244 OSExchangeData::FilenameToURLPolicy policy,
245 GURL* url,
246 base::string16* title) const {
247 std::vector< ::Atom> url_atoms = ui::GetURLAtomsFrom(&atom_cache_);
248 std::vector< ::Atom> requested_types;
249 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
250
251 ui::SelectionData data(format_map_.GetFirstOf(requested_types));
252 if (data.IsValid()) {
253 // TODO(erg): Technically, both of these forms can accept multiple URLs,
254 // but that doesn't match the assumptions of the rest of the system which
255 // expect single types.
256
257 if (data.GetType() == atom_cache_.GetAtom(kMimeTypeMozillaURL)) {
258 // Mozilla URLs are (UTF16: URL, newline, title).
259 base::string16 unparsed;
260 data.AssignTo(&unparsed);
261
262 std::vector<base::string16> tokens;
263 size_t num_tokens = Tokenize(unparsed, base::ASCIIToUTF16("\n"), &tokens);
264 if (num_tokens > 0) {
265 if (num_tokens > 1)
266 *title = tokens[1];
267 else
268 *title = base::string16();
269
270 *url = GURL(tokens[0]);
271 return true;
272 }
273 } else if (data.GetType() == atom_cache_.GetAtom(
274 Clipboard::kMimeTypeURIList)) {
275 std::vector<std::string> tokens = ui::ParseURIList(data);
276 for (std::vector<std::string>::const_iterator it = tokens.begin();
277 it != tokens.end(); ++it) {
278 GURL test_url(*it);
279 if (!test_url.SchemeIsFile() ||
280 policy == OSExchangeData::CONVERT_FILENAMES) {
281 *url = test_url;
282 *title = base::string16();
283 return true;
284 }
285 }
286 }
287 }
288
289 return false;
290 }
291
292 bool OSExchangeDataProviderAuraX11::GetFilename(base::FilePath* path) const {
293 std::vector<FileInfo> filenames;
294 if (GetFilenames(&filenames)) {
295 *path = filenames.front().path;
296 return true;
297 }
298
299 return false;
300 }
301
302 bool OSExchangeDataProviderAuraX11::GetFilenames(
303 std::vector<FileInfo>* filenames) const {
304 std::vector< ::Atom> url_atoms = ui::GetURIListAtomsFrom(&atom_cache_);
305 std::vector< ::Atom> requested_types;
306 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
307
308 filenames->clear();
309 ui::SelectionData data(format_map_.GetFirstOf(requested_types));
310 if (data.IsValid()) {
311 std::vector<std::string> tokens = ui::ParseURIList(data);
312 for (std::vector<std::string>::const_iterator it = tokens.begin();
313 it != tokens.end(); ++it) {
314 GURL url(*it);
315 base::FilePath file_path;
316 if (url.SchemeIsFile() && net::FileURLToFilePath(url, &file_path)) {
317 filenames->push_back(FileInfo(file_path, base::FilePath()));
318 }
319 }
320 }
321
322 return !filenames->empty();
323 }
324
325 bool OSExchangeDataProviderAuraX11::GetPickledData(
326 const OSExchangeData::CustomFormat& format,
327 Pickle* pickle) const {
328 std::vector< ::Atom> requested_types;
329 requested_types.push_back(atom_cache_.GetAtom(format.ToString().c_str()));
330
331 ui::SelectionData data(format_map_.GetFirstOf(requested_types));
332 if (data.IsValid()) {
333 // Note that the pickle object on the right hand side of the assignment
334 // only refers to the bytes in |data|. The assignment copies the data.
335 *pickle = Pickle(reinterpret_cast<const char*>(data.GetData()),
336 static_cast<int>(data.GetSize()));
337 return true;
338 }
339
340 return false;
341 }
342
343 bool OSExchangeDataProviderAuraX11::HasString() const {
344 std::vector< ::Atom> text_atoms = ui::GetTextAtomsFrom(&atom_cache_);
345 std::vector< ::Atom> requested_types;
346 ui::GetAtomIntersection(text_atoms, GetTargets(), &requested_types);
347 return !requested_types.empty() && !HasFile();
348 }
349
350 bool OSExchangeDataProviderAuraX11::HasURL(
351 OSExchangeData::FilenameToURLPolicy policy) const {
352 std::vector< ::Atom> url_atoms = ui::GetURLAtomsFrom(&atom_cache_);
353 std::vector< ::Atom> requested_types;
354 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
355
356 if (requested_types.empty())
357 return false;
358
359 // The Linux desktop doesn't differentiate between files and URLs like
360 // Windows does and stuffs all the data into one mime type.
361 ui::SelectionData data(format_map_.GetFirstOf(requested_types));
362 if (data.IsValid()) {
363 if (data.GetType() == atom_cache_.GetAtom(kMimeTypeMozillaURL)) {
364 // File managers shouldn't be using this type, so this is a URL.
365 return true;
366 } else if (data.GetType() == atom_cache_.GetAtom(
367 ui::Clipboard::kMimeTypeURIList)) {
368 std::vector<std::string> tokens = ui::ParseURIList(data);
369 for (std::vector<std::string>::const_iterator it = tokens.begin();
370 it != tokens.end(); ++it) {
371 if (!GURL(*it).SchemeIsFile() ||
372 policy == OSExchangeData::CONVERT_FILENAMES)
373 return true;
374 }
375
376 return false;
377 }
378 }
379
380 return false;
381 }
382
383 bool OSExchangeDataProviderAuraX11::HasFile() const {
384 std::vector< ::Atom> url_atoms = ui::GetURIListAtomsFrom(&atom_cache_);
385 std::vector< ::Atom> requested_types;
386 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
387
388 if (requested_types.empty())
389 return false;
390
391 // To actually answer whether we have a file, we need to look through the
392 // contents of the kMimeTypeURIList type, and see if any of them are file://
393 // URIs.
394 ui::SelectionData data(format_map_.GetFirstOf(requested_types));
395 if (data.IsValid()) {
396 std::vector<std::string> tokens = ui::ParseURIList(data);
397 for (std::vector<std::string>::const_iterator it = tokens.begin();
398 it != tokens.end(); ++it) {
399 GURL url(*it);
400 base::FilePath file_path;
401 if (url.SchemeIsFile() && net::FileURLToFilePath(url, &file_path))
402 return true;
403 }
404 }
405
406 return false;
407 }
408
409 bool OSExchangeDataProviderAuraX11::HasCustomFormat(
410 const OSExchangeData::CustomFormat& format) const {
411 std::vector< ::Atom> url_atoms;
412 url_atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str()));
413 std::vector< ::Atom> requested_types;
414 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
415
416 return !requested_types.empty();
417 }
418
419 void OSExchangeDataProviderAuraX11::SetFileContents(
420 const base::FilePath& filename,
421 const std::string& file_contents) {
422 DCHECK(!filename.empty());
423 DCHECK(format_map_.end() ==
424 format_map_.find(atom_cache_.GetAtom(kMimeTypeMozillaURL)));
425
426 file_contents_name_ = filename;
427
428 // Direct save handling is a complicated juggling affair between this class,
429 // SelectionFormat, and DesktopDragDropClientAuraX11. The general idea behind
430 // the protocol is this:
431 // - The source window sets its XdndDirectSave0 window property to the
432 // proposed filename.
433 // - When a target window receives the drop, it updates the XdndDirectSave0
434 // property on the source window to the filename it would like the contents
435 // to be saved to and then requests the XdndDirectSave0 type from the
436 // source.
437 // - The source is supposed to copy the file here and return success (S),
438 // failure (F), or error (E).
439 // - In this case, failure means the destination should try to populate the
440 // file itself by copying the data from application/octet-stream. To make
441 // things simpler for Chrome, we always 'fail' and let the destination do
442 // the work.
443 std::string failure("F");
444 format_map_.Insert(
445 atom_cache_.GetAtom("XdndDirectSave0"),
446 scoped_refptr<base::RefCountedMemory>(
447 base::RefCountedString::TakeString(&failure)));
448 std::string file_contents_copy = file_contents;
449 format_map_.Insert(
450 atom_cache_.GetAtom("application/octet-stream"),
451 scoped_refptr<base::RefCountedMemory>(
452 base::RefCountedString::TakeString(&file_contents_copy)));
453 }
454
455 void OSExchangeDataProviderAuraX11::SetHtml(const base::string16& html,
456 const GURL& base_url) {
457 std::vector<unsigned char> bytes;
458 // Manually jam a UTF16 BOM into bytes because otherwise, other programs will
459 // assume UTF-8.
460 bytes.push_back(0xFF);
461 bytes.push_back(0xFE);
462 ui::AddString16ToVector(html, &bytes);
463 scoped_refptr<base::RefCountedMemory> mem(
464 base::RefCountedBytes::TakeVector(&bytes));
465
466 format_map_.Insert(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML), mem);
467 }
468
469 bool OSExchangeDataProviderAuraX11::GetHtml(base::string16* html,
470 GURL* base_url) const {
471 std::vector< ::Atom> url_atoms;
472 url_atoms.push_back(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML));
473 std::vector< ::Atom> requested_types;
474 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
475
476 ui::SelectionData data(format_map_.GetFirstOf(requested_types));
477 if (data.IsValid()) {
478 *html = data.GetHtml();
479 *base_url = GURL();
480 return true;
481 }
482
483 return false;
484 }
485
486 bool OSExchangeDataProviderAuraX11::HasHtml() const {
487 std::vector< ::Atom> url_atoms;
488 url_atoms.push_back(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML));
489 std::vector< ::Atom> requested_types;
490 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
491
492 return !requested_types.empty();
493 }
494
495 void OSExchangeDataProviderAuraX11::SetDragImage(
496 const gfx::ImageSkia& image,
497 const gfx::Vector2d& cursor_offset) {
498 drag_image_ = image;
499 drag_image_offset_ = cursor_offset;
500 }
501
502 const gfx::ImageSkia& OSExchangeDataProviderAuraX11::GetDragImage() const {
503 return drag_image_;
504 }
505
506 const gfx::Vector2d& OSExchangeDataProviderAuraX11::GetDragImageOffset() const {
507 return drag_image_offset_;
508 }
509
510 bool OSExchangeDataProviderAuraX11::CanDispatchEvent(
511 const PlatformEvent& event) {
512 return event->xany.window == x_window_;
513 }
514
515 uint32_t OSExchangeDataProviderAuraX11::DispatchEvent(
516 const PlatformEvent& event) {
517 XEvent* xev = event;
518 switch (xev->type) {
519 case SelectionRequest:
520 selection_owner_.OnSelectionRequest(*xev);
521 return ui::POST_DISPATCH_STOP_PROPAGATION;
522 default:
523 NOTIMPLEMENTED();
524 }
525 return ui::POST_DISPATCH_NONE;
526 }
527
528 bool OSExchangeDataProviderAuraX11::GetPlainTextURL(GURL* url) const {
529 base::string16 text;
530 if (GetString(&text)) {
531 GURL test_url(text);
532 if (test_url.is_valid()) {
533 *url = test_url;
534 return true;
535 }
536 }
537
538 return false;
539 }
540
541 std::vector< ::Atom> OSExchangeDataProviderAuraX11::GetTargets() const {
542 return format_map_.GetTypes();
543 }
544
545 ///////////////////////////////////////////////////////////////////////////////
546 // OSExchangeData, public:
547
548 // static
549 OSExchangeData::Provider* OSExchangeData::CreateProvider() {
550 return new OSExchangeDataProviderAuraX11();
551 }
552
553 } // namespace ui
OLDNEW
« no previous file with comments | « ui/base/dragdrop/os_exchange_data_provider_aurax11.h ('k') | ui/base/dragdrop/os_exchange_data_provider_aurax11_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698