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