Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / base / dragdrop / os_exchange_data_provider_aurax11.cc
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   std::string utf8 = base::UTF16ToUTF8(text_data);
131   scoped_refptr<base::RefCountedMemory> mem(
132       base::RefCountedString::TakeString(&utf8));
133
134   format_map_.Insert(atom_cache_.GetAtom(Clipboard::kMimeTypeText), mem);
135   format_map_.Insert(atom_cache_.GetAtom(kText), mem);
136   format_map_.Insert(atom_cache_.GetAtom(kString), mem);
137   format_map_.Insert(atom_cache_.GetAtom(kUtf8String), mem);
138 }
139
140 void OSExchangeDataProviderAuraX11::SetURL(const GURL& url,
141                                            const base::string16& title) {
142   // TODO(dcheng): The original GTK code tries very hard to avoid writing out an
143   // empty title. Is this necessary?
144   if (url.is_valid()) {
145     // Mozilla's URL format: (UTF16: URL, newline, title)
146     base::string16 spec = base::UTF8ToUTF16(url.spec());
147
148     std::vector<unsigned char> data;
149     ui::AddString16ToVector(spec, &data);
150     ui::AddString16ToVector(base::ASCIIToUTF16("\n"), &data);
151     ui::AddString16ToVector(title, &data);
152     scoped_refptr<base::RefCountedMemory> mem(
153         base::RefCountedBytes::TakeVector(&data));
154
155     format_map_.Insert(atom_cache_.GetAtom(kMimeTypeMozillaURL), mem);
156
157     // Set a string fallback as well.
158     SetString(spec);
159
160     // Return early if this drag already contains file contents (this implies
161     // that file contents must be populated before URLs). Nautilus (and possibly
162     // other file managers) prefer _NETSCAPE_URL over the X Direct Save
163     // protocol, but we want to prioritize XDS in this case.
164     if (!file_contents_name_.empty())
165       return;
166
167     // Set _NETSCAPE_URL for file managers like Nautilus that use it as a hint
168     // to create a link to the URL. Setting text/uri-list doesn't work because
169     // Nautilus will fetch and copy the contents of the URL to the drop target
170     // instead of linking...
171     // Format is UTF8: URL + "\n" + title.
172     std::string netscape_url = url.spec();
173     netscape_url += "\n";
174     netscape_url += base::UTF16ToUTF8(title);
175     format_map_.Insert(atom_cache_.GetAtom(kNetscapeURL),
176                        scoped_refptr<base::RefCountedMemory>(
177                            base::RefCountedString::TakeString(&netscape_url)));
178   }
179 }
180
181 void OSExchangeDataProviderAuraX11::SetFilename(const base::FilePath& path) {
182   std::vector<FileInfo> data;
183   data.push_back(FileInfo(path, base::FilePath()));
184   SetFilenames(data);
185 }
186
187 void OSExchangeDataProviderAuraX11::SetFilenames(
188     const std::vector<FileInfo>& filenames) {
189   std::vector<std::string> paths;
190   for (std::vector<FileInfo>::const_iterator it = filenames.begin();
191        it != filenames.end();
192        ++it) {
193     std::string url_spec = net::FilePathToFileURL(it->path).spec();
194     if (!url_spec.empty())
195       paths.push_back(url_spec);
196   }
197
198   std::string joined_data = JoinString(paths, '\n');
199   scoped_refptr<base::RefCountedMemory> mem(
200       base::RefCountedString::TakeString(&joined_data));
201   format_map_.Insert(atom_cache_.GetAtom(Clipboard::kMimeTypeURIList), mem);
202 }
203
204 void OSExchangeDataProviderAuraX11::SetPickledData(
205     const OSExchangeData::CustomFormat& format,
206     const Pickle& pickle) {
207   const unsigned char* data =
208       reinterpret_cast<const unsigned char*>(pickle.data());
209
210   std::vector<unsigned char> bytes;
211   bytes.insert(bytes.end(), data, data + pickle.size());
212   scoped_refptr<base::RefCountedMemory> mem(
213       base::RefCountedBytes::TakeVector(&bytes));
214
215   format_map_.Insert(atom_cache_.GetAtom(format.ToString().c_str()), mem);
216 }
217
218 bool OSExchangeDataProviderAuraX11::GetString(base::string16* result) const {
219   if (HasFile()) {
220     // Various Linux file managers both pass a list of file:// URIs and set the
221     // string representation to the URI. We explicitly don't want to return use
222     // this representation.
223     return false;
224   }
225
226   std::vector< ::Atom> text_atoms = ui::GetTextAtomsFrom(&atom_cache_);
227   std::vector< ::Atom> requested_types;
228   ui::GetAtomIntersection(text_atoms, GetTargets(), &requested_types);
229
230   ui::SelectionData data(format_map_.GetFirstOf(requested_types));
231   if (data.IsValid()) {
232     std::string text = data.GetText();
233     *result = base::UTF8ToUTF16(text);
234     return true;
235   }
236
237   return false;
238 }
239
240 bool OSExchangeDataProviderAuraX11::GetURLAndTitle(
241     OSExchangeData::FilenameToURLPolicy policy,
242     GURL* url,
243     base::string16* title) const {
244   std::vector< ::Atom> url_atoms = ui::GetURLAtomsFrom(&atom_cache_);
245   std::vector< ::Atom> requested_types;
246   ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
247
248   ui::SelectionData data(format_map_.GetFirstOf(requested_types));
249   if (data.IsValid()) {
250     // TODO(erg): Technically, both of these forms can accept multiple URLs,
251     // but that doesn't match the assumptions of the rest of the system which
252     // expect single types.
253
254     if (data.GetType() == atom_cache_.GetAtom(kMimeTypeMozillaURL)) {
255       // Mozilla URLs are (UTF16: URL, newline, title).
256       base::string16 unparsed;
257       data.AssignTo(&unparsed);
258
259       std::vector<base::string16> tokens;
260       size_t num_tokens = Tokenize(unparsed, base::ASCIIToUTF16("\n"), &tokens);
261       if (num_tokens > 0) {
262         if (num_tokens > 1)
263           *title = tokens[1];
264         else
265           *title = base::string16();
266
267         *url = GURL(tokens[0]);
268         return true;
269       }
270     } else if (data.GetType() == atom_cache_.GetAtom(
271                    Clipboard::kMimeTypeURIList)) {
272       std::vector<std::string> tokens = ui::ParseURIList(data);
273       for (std::vector<std::string>::const_iterator it = tokens.begin();
274            it != tokens.end(); ++it) {
275         GURL test_url(*it);
276         if (!test_url.SchemeIsFile() ||
277             policy == OSExchangeData::CONVERT_FILENAMES) {
278           *url = test_url;
279           *title = base::string16();
280           return true;
281         }
282       }
283     }
284   }
285
286   return false;
287 }
288
289 bool OSExchangeDataProviderAuraX11::GetFilename(base::FilePath* path) const {
290   std::vector<FileInfo> filenames;
291   if (GetFilenames(&filenames)) {
292     *path = filenames.front().path;
293     return true;
294   }
295
296   return false;
297 }
298
299 bool OSExchangeDataProviderAuraX11::GetFilenames(
300     std::vector<FileInfo>* filenames) const {
301   std::vector< ::Atom> url_atoms = ui::GetURIListAtomsFrom(&atom_cache_);
302   std::vector< ::Atom> requested_types;
303   ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
304
305   filenames->clear();
306   ui::SelectionData data(format_map_.GetFirstOf(requested_types));
307   if (data.IsValid()) {
308     std::vector<std::string> tokens = ui::ParseURIList(data);
309     for (std::vector<std::string>::const_iterator it = tokens.begin();
310          it != tokens.end(); ++it) {
311       GURL url(*it);
312       base::FilePath file_path;
313       if (url.SchemeIsFile() && net::FileURLToFilePath(url, &file_path)) {
314         filenames->push_back(FileInfo(file_path, base::FilePath()));
315       }
316     }
317   }
318
319   return !filenames->empty();
320 }
321
322 bool OSExchangeDataProviderAuraX11::GetPickledData(
323     const OSExchangeData::CustomFormat& format,
324     Pickle* pickle) const {
325   std::vector< ::Atom> requested_types;
326   requested_types.push_back(atom_cache_.GetAtom(format.ToString().c_str()));
327
328   ui::SelectionData data(format_map_.GetFirstOf(requested_types));
329   if (data.IsValid()) {
330     // Note that the pickle object on the right hand side of the assignment
331     // only refers to the bytes in |data|. The assignment copies the data.
332     *pickle = Pickle(reinterpret_cast<const char*>(data.GetData()),
333                      static_cast<int>(data.GetSize()));
334     return true;
335   }
336
337   return false;
338 }
339
340 bool OSExchangeDataProviderAuraX11::HasString() const {
341   std::vector< ::Atom> text_atoms = ui::GetTextAtomsFrom(&atom_cache_);
342   std::vector< ::Atom> requested_types;
343   ui::GetAtomIntersection(text_atoms, GetTargets(), &requested_types);
344   return !requested_types.empty() && !HasFile();
345 }
346
347 bool OSExchangeDataProviderAuraX11::HasURL(
348     OSExchangeData::FilenameToURLPolicy policy) const {
349   std::vector< ::Atom> url_atoms = ui::GetURLAtomsFrom(&atom_cache_);
350   std::vector< ::Atom> requested_types;
351   ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
352
353   if (requested_types.empty())
354     return false;
355
356   // The Linux desktop doesn't differentiate between files and URLs like
357   // Windows does and stuffs all the data into one mime type.
358   ui::SelectionData data(format_map_.GetFirstOf(requested_types));
359   if (data.IsValid()) {
360     if (data.GetType() == atom_cache_.GetAtom(kMimeTypeMozillaURL)) {
361       // File managers shouldn't be using this type, so this is a URL.
362       return true;
363     } else if (data.GetType() == atom_cache_.GetAtom(
364         ui::Clipboard::kMimeTypeURIList)) {
365       std::vector<std::string> tokens = ui::ParseURIList(data);
366       for (std::vector<std::string>::const_iterator it = tokens.begin();
367            it != tokens.end(); ++it) {
368         if (!GURL(*it).SchemeIsFile() ||
369             policy == OSExchangeData::CONVERT_FILENAMES)
370           return true;
371       }
372
373       return false;
374     }
375   }
376
377   return false;
378 }
379
380 bool OSExchangeDataProviderAuraX11::HasFile() const {
381   std::vector< ::Atom> url_atoms = ui::GetURIListAtomsFrom(&atom_cache_);
382   std::vector< ::Atom> requested_types;
383   ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
384
385   if (requested_types.empty())
386     return false;
387
388   // To actually answer whether we have a file, we need to look through the
389   // contents of the kMimeTypeURIList type, and see if any of them are file://
390   // URIs.
391   ui::SelectionData data(format_map_.GetFirstOf(requested_types));
392   if (data.IsValid()) {
393     std::vector<std::string> tokens = ui::ParseURIList(data);
394     for (std::vector<std::string>::const_iterator it = tokens.begin();
395          it != tokens.end(); ++it) {
396       GURL url(*it);
397       base::FilePath file_path;
398       if (url.SchemeIsFile() && net::FileURLToFilePath(url, &file_path))
399         return true;
400     }
401   }
402
403   return false;
404 }
405
406 bool OSExchangeDataProviderAuraX11::HasCustomFormat(
407     const OSExchangeData::CustomFormat& format) const {
408   std::vector< ::Atom> url_atoms;
409   url_atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str()));
410   std::vector< ::Atom> requested_types;
411   ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
412
413   return !requested_types.empty();
414 }
415
416 void OSExchangeDataProviderAuraX11::SetFileContents(
417     const base::FilePath& filename,
418     const std::string& file_contents) {
419   DCHECK(!filename.empty());
420   DCHECK(format_map_.end() ==
421          format_map_.find(atom_cache_.GetAtom(kMimeTypeMozillaURL)));
422
423   file_contents_name_ = filename;
424
425   // Direct save handling is a complicated juggling affair between this class,
426   // SelectionFormat, and DesktopDragDropClientAuraX11. The general idea behind
427   // the protocol is this:
428   // - The source window sets its XdndDirectSave0 window property to the
429   //   proposed filename.
430   // - When a target window receives the drop, it updates the XdndDirectSave0
431   //   property on the source window to the filename it would like the contents
432   //   to be saved to and then requests the XdndDirectSave0 type from the
433   //   source.
434   // - The source is supposed to copy the file here and return success (S),
435   //   failure (F), or error (E).
436   // - In this case, failure means the destination should try to populate the
437   //   file itself by copying the data from application/octet-stream. To make
438   //   things simpler for Chrome, we always 'fail' and let the destination do
439   //   the work.
440   std::string failure("F");
441   format_map_.Insert(
442       atom_cache_.GetAtom("XdndDirectSave0"),
443                           scoped_refptr<base::RefCountedMemory>(
444                               base::RefCountedString::TakeString(&failure)));
445   std::string file_contents_copy = file_contents;
446   format_map_.Insert(
447       atom_cache_.GetAtom("application/octet-stream"),
448       scoped_refptr<base::RefCountedMemory>(
449           base::RefCountedString::TakeString(&file_contents_copy)));
450 }
451
452 void OSExchangeDataProviderAuraX11::SetHtml(const base::string16& html,
453                                             const GURL& base_url) {
454   std::vector<unsigned char> bytes;
455   // Manually jam a UTF16 BOM into bytes because otherwise, other programs will
456   // assume UTF-8.
457   bytes.push_back(0xFF);
458   bytes.push_back(0xFE);
459   ui::AddString16ToVector(html, &bytes);
460   scoped_refptr<base::RefCountedMemory> mem(
461       base::RefCountedBytes::TakeVector(&bytes));
462
463   format_map_.Insert(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML), mem);
464 }
465
466 bool OSExchangeDataProviderAuraX11::GetHtml(base::string16* html,
467                                             GURL* base_url) const {
468   std::vector< ::Atom> url_atoms;
469   url_atoms.push_back(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML));
470   std::vector< ::Atom> requested_types;
471   ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
472
473   ui::SelectionData data(format_map_.GetFirstOf(requested_types));
474   if (data.IsValid()) {
475     *html = data.GetHtml();
476     *base_url = GURL();
477     return true;
478   }
479
480   return false;
481 }
482
483 bool OSExchangeDataProviderAuraX11::HasHtml() const {
484   std::vector< ::Atom> url_atoms;
485   url_atoms.push_back(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML));
486   std::vector< ::Atom> requested_types;
487   ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
488
489   return !requested_types.empty();
490 }
491
492 void OSExchangeDataProviderAuraX11::SetDragImage(
493     const gfx::ImageSkia& image,
494     const gfx::Vector2d& cursor_offset) {
495   drag_image_ = image;
496   drag_image_offset_ = cursor_offset;
497 }
498
499 const gfx::ImageSkia& OSExchangeDataProviderAuraX11::GetDragImage() const {
500   return drag_image_;
501 }
502
503 const gfx::Vector2d& OSExchangeDataProviderAuraX11::GetDragImageOffset() const {
504   return drag_image_offset_;
505 }
506
507 bool OSExchangeDataProviderAuraX11::CanDispatchEvent(
508     const PlatformEvent& event) {
509   return event->xany.window == x_window_;
510 }
511
512 uint32_t OSExchangeDataProviderAuraX11::DispatchEvent(
513     const PlatformEvent& event) {
514   XEvent* xev = event;
515   switch (xev->type) {
516     case SelectionRequest:
517       selection_owner_.OnSelectionRequest(xev->xselectionrequest);
518       return ui::POST_DISPATCH_STOP_PROPAGATION;
519     default:
520       NOTIMPLEMENTED();
521   }
522   return ui::POST_DISPATCH_NONE;
523 }
524
525 bool OSExchangeDataProviderAuraX11::GetPlainTextURL(GURL* url) const {
526   base::string16 text;
527   if (GetString(&text)) {
528     GURL test_url(text);
529     if (test_url.is_valid()) {
530       *url = test_url;
531       return true;
532     }
533   }
534
535   return false;
536 }
537
538 std::vector< ::Atom> OSExchangeDataProviderAuraX11::GetTargets() const {
539   return format_map_.GetTypes();
540 }
541
542 ///////////////////////////////////////////////////////////////////////////////
543 // OSExchangeData, public:
544
545 // static
546 OSExchangeData::Provider* OSExchangeData::CreateProvider() {
547   return new OSExchangeDataProviderAuraX11();
548 }
549
550 }  // namespace ui