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/message_loop/message_pump_x11.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "net/base/net_util.h"
13 #include "ui/base/clipboard/clipboard.h"
14 #include "ui/base/clipboard/scoped_clipboard_writer.h"
15 #include "ui/base/x/selection_utils.h"
16 #include "ui/base/x/x11_util.h"
18 // Note: the GetBlah() methods are used immediately by the
19 // web_contents_view_aura.cc:PrepareDropData(), while the omnibox is a
20 // little more discriminating and calls HasBlah() before trying to get the
27 const char kDndSelection
[] = "XdndSelection";
29 const char* kAtomsToCache
[] = {
34 Clipboard::kMimeTypeURIList
,
36 Clipboard::kMimeTypeText
,
42 OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11(
44 const SelectionFormatMap
& selection
)
45 : x_display_(gfx::GetXDisplay()),
46 x_root_window_(DefaultRootWindow(x_display_
)),
49 atom_cache_(x_display_
, kAtomsToCache
),
50 format_map_(selection
),
51 selection_owner_(x_display_
, x_window_
,
52 atom_cache_
.GetAtom(kDndSelection
)) {
53 // We don't know all possible MIME types at compile time.
54 atom_cache_
.allow_uncached_atoms();
57 OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11()
58 : x_display_(gfx::GetXDisplay()),
59 x_root_window_(DefaultRootWindow(x_display_
)),
61 x_window_(XCreateWindow(
64 -100, -100, 10, 10, // x, y, width, height
66 CopyFromParent
, // depth
68 CopyFromParent
, // visual
71 atom_cache_(x_display_
, kAtomsToCache
),
73 selection_owner_(x_display_
, x_window_
,
74 atom_cache_
.GetAtom(kDndSelection
)) {
75 // We don't know all possible MIME types at compile time.
76 atom_cache_
.allow_uncached_atoms();
78 XStoreName(x_display_
, x_window_
, "Chromium Drag & Drop Window");
80 base::MessagePumpX11::Current()->AddDispatcherForWindow(this, x_window_
);
83 OSExchangeDataProviderAuraX11::~OSExchangeDataProviderAuraX11() {
85 base::MessagePumpX11::Current()->RemoveDispatcherForWindow(x_window_
);
86 XDestroyWindow(x_display_
, x_window_
);
90 void OSExchangeDataProviderAuraX11::TakeOwnershipOfSelection() const {
91 selection_owner_
.TakeOwnershipOfSelection(format_map_
);
94 void OSExchangeDataProviderAuraX11::RetrieveTargets(
95 std::vector
<Atom
>* targets
) const {
96 selection_owner_
.RetrieveTargets(targets
);
99 SelectionFormatMap
OSExchangeDataProviderAuraX11::GetFormatMap() const {
100 // We return the |selection_owner_|'s format map instead of our own in case
101 // ours has been modified since TakeOwnershipOfSelection() was called.
102 return selection_owner_
.selection_format_map();
105 OSExchangeData::Provider
* OSExchangeDataProviderAuraX11::Clone() const {
106 OSExchangeDataProviderAuraX11
* ret
= new OSExchangeDataProviderAuraX11();
107 ret
->format_map_
= format_map_
;
111 void OSExchangeDataProviderAuraX11::SetString(const base::string16
& text_data
) {
112 std::string utf8
= base::UTF16ToUTF8(text_data
);
113 scoped_refptr
<base::RefCountedMemory
> mem(
114 base::RefCountedString::TakeString(&utf8
));
116 format_map_
.Insert(atom_cache_
.GetAtom(Clipboard::kMimeTypeText
), mem
);
117 format_map_
.Insert(atom_cache_
.GetAtom(kText
), mem
);
118 format_map_
.Insert(atom_cache_
.GetAtom(kString
), mem
);
119 format_map_
.Insert(atom_cache_
.GetAtom(kUtf8String
), mem
);
122 void OSExchangeDataProviderAuraX11::SetURL(const GURL
& url
,
123 const base::string16
& title
) {
124 // Mozilla's URL format: (UTF16: URL, newline, title)
125 if (url
.is_valid()) {
126 base::string16 spec
= base::UTF8ToUTF16(url
.spec());
128 std::vector
<unsigned char> data
;
129 ui::AddString16ToVector(spec
, &data
);
130 ui::AddString16ToVector(base::ASCIIToUTF16("\n"), &data
);
131 ui::AddString16ToVector(title
, &data
);
132 scoped_refptr
<base::RefCountedMemory
> mem(
133 base::RefCountedBytes::TakeVector(&data
));
135 format_map_
.Insert(atom_cache_
.GetAtom(kMimeTypeMozillaURL
), mem
);
141 void OSExchangeDataProviderAuraX11::SetFilename(const base::FilePath
& path
) {
142 std::vector
<OSExchangeData::FileInfo
> data
;
143 data
.push_back(OSExchangeData::FileInfo(path
, base::FilePath()));
147 void OSExchangeDataProviderAuraX11::SetFilenames(
148 const std::vector
<OSExchangeData::FileInfo
>& filenames
) {
149 std::vector
<std::string
> paths
;
150 for (std::vector
<OSExchangeData::FileInfo
>::const_iterator it
=
151 filenames
.begin(); it
!= filenames
.end(); ++it
) {
152 std::string url_spec
= net::FilePathToFileURL(it
->path
).spec();
153 if (!url_spec
.empty())
154 paths
.push_back(url_spec
);
157 std::string joined_data
= JoinString(paths
, '\n');
158 scoped_refptr
<base::RefCountedMemory
> mem(
159 base::RefCountedString::TakeString(&joined_data
));
160 format_map_
.Insert(atom_cache_
.GetAtom(Clipboard::kMimeTypeURIList
), mem
);
163 void OSExchangeDataProviderAuraX11::SetPickledData(
164 const OSExchangeData::CustomFormat
& format
,
165 const Pickle
& pickle
) {
166 const unsigned char* data
=
167 reinterpret_cast<const unsigned char*>(pickle
.data());
169 std::vector
<unsigned char> bytes
;
170 bytes
.insert(bytes
.end(), data
, data
+ pickle
.size());
171 scoped_refptr
<base::RefCountedMemory
> mem(
172 base::RefCountedBytes::TakeVector(&bytes
));
174 format_map_
.Insert(atom_cache_
.GetAtom(format
.ToString().c_str()), mem
);
177 bool OSExchangeDataProviderAuraX11::GetString(base::string16
* result
) const {
178 std::vector
< ::Atom
> text_atoms
= ui::GetTextAtomsFrom(&atom_cache_
);
179 std::vector
< ::Atom
> requested_types
;
180 ui::GetAtomIntersection(text_atoms
, GetTargets(), &requested_types
);
182 ui::SelectionData
data(format_map_
.GetFirstOf(requested_types
));
183 if (data
.IsValid()) {
184 std::string text
= data
.GetText();
185 *result
= base::UTF8ToUTF16(text
);
192 bool OSExchangeDataProviderAuraX11::GetURLAndTitle(
194 base::string16
* title
) const {
195 std::vector
< ::Atom
> url_atoms
= ui::GetURLAtomsFrom(&atom_cache_
);
196 std::vector
< ::Atom
> requested_types
;
197 ui::GetAtomIntersection(url_atoms
, GetTargets(), &requested_types
);
199 ui::SelectionData
data(format_map_
.GetFirstOf(requested_types
));
200 if (data
.IsValid()) {
201 // TODO(erg): Technically, both of these forms can accept multiple URLs,
202 // but that doesn't match the assumptions of the rest of the system which
203 // expect single types.
205 if (data
.GetType() == atom_cache_
.GetAtom(kMimeTypeMozillaURL
)) {
206 // Mozilla URLs are (UTF16: URL, newline, title).
207 base::string16 unparsed
;
208 data
.AssignTo(&unparsed
);
210 std::vector
<base::string16
> tokens
;
211 size_t num_tokens
= Tokenize(unparsed
, base::ASCIIToUTF16("\n"), &tokens
);
212 if (num_tokens
> 0) {
216 *title
= base::string16();
218 *url
= GURL(tokens
[0]);
221 } else if (data
.GetType() == atom_cache_
.GetAtom(
222 Clipboard::kMimeTypeURIList
)) {
223 std::vector
<std::string
> tokens
= ui::ParseURIList(data
);
224 for (std::vector
<std::string
>::const_iterator it
= tokens
.begin();
225 it
!= tokens
.end(); ++it
) {
227 if (!test_url
.SchemeIsFile()) {
229 *title
= base::string16();
239 bool OSExchangeDataProviderAuraX11::GetFilename(base::FilePath
* path
) const {
240 std::vector
<OSExchangeData::FileInfo
> filenames
;
241 if (GetFilenames(&filenames
)) {
242 *path
= filenames
.front().path
;
249 bool OSExchangeDataProviderAuraX11::GetFilenames(
250 std::vector
<OSExchangeData::FileInfo
>* filenames
) const {
251 std::vector
< ::Atom
> url_atoms
= ui::GetURIListAtomsFrom(&atom_cache_
);
252 std::vector
< ::Atom
> requested_types
;
253 ui::GetAtomIntersection(url_atoms
, GetTargets(), &requested_types
);
256 ui::SelectionData
data(format_map_
.GetFirstOf(requested_types
));
257 if (data
.IsValid()) {
258 std::vector
<std::string
> tokens
= ui::ParseURIList(data
);
259 for (std::vector
<std::string
>::const_iterator it
= tokens
.begin();
260 it
!= tokens
.end(); ++it
) {
262 base::FilePath file_path
;
263 if (url
.SchemeIsFile() && net::FileURLToFilePath(url
, &file_path
)) {
264 filenames
->push_back(OSExchangeData::FileInfo(file_path
,
270 return !filenames
->empty();
273 bool OSExchangeDataProviderAuraX11::GetPickledData(
274 const OSExchangeData::CustomFormat
& format
,
275 Pickle
* pickle
) const {
276 std::vector
< ::Atom
> requested_types
;
277 requested_types
.push_back(atom_cache_
.GetAtom(format
.ToString().c_str()));
279 ui::SelectionData
data(format_map_
.GetFirstOf(requested_types
));
280 if (data
.IsValid()) {
281 // Note that the pickle object on the right hand side of the assignment
282 // only refers to the bytes in |data|. The assignment copies the data.
283 *pickle
= Pickle(reinterpret_cast<const char*>(data
.GetData()),
284 static_cast<int>(data
.GetSize()));
291 bool OSExchangeDataProviderAuraX11::HasString() const {
292 std::vector
< ::Atom
> text_atoms
= ui::GetTextAtomsFrom(&atom_cache_
);
293 std::vector
< ::Atom
> requested_types
;
294 ui::GetAtomIntersection(text_atoms
, GetTargets(), &requested_types
);
295 return !requested_types
.empty();
298 bool OSExchangeDataProviderAuraX11::HasURL() const {
299 std::vector
< ::Atom
> url_atoms
= ui::GetURLAtomsFrom(&atom_cache_
);
300 std::vector
< ::Atom
> requested_types
;
301 ui::GetAtomIntersection(url_atoms
, GetTargets(), &requested_types
);
303 if (requested_types
.empty())
306 // The Linux desktop doesn't differentiate between files and URLs like
307 // Windows does and stuffs all the data into one mime type.
308 ui::SelectionData
data(format_map_
.GetFirstOf(requested_types
));
309 if (data
.IsValid()) {
310 if (data
.GetType() == atom_cache_
.GetAtom(kMimeTypeMozillaURL
)) {
311 // File managers shouldn't be using this type, so this is a URL.
313 } else if (data
.GetType() == atom_cache_
.GetAtom(
314 ui::Clipboard::kMimeTypeURIList
)) {
315 std::vector
<std::string
> tokens
= ui::ParseURIList(data
);
316 for (std::vector
<std::string
>::const_iterator it
= tokens
.begin();
317 it
!= tokens
.end(); ++it
) {
318 if (!GURL(*it
).SchemeIsFile())
329 bool OSExchangeDataProviderAuraX11::HasFile() const {
330 std::vector
< ::Atom
> url_atoms
= ui::GetURIListAtomsFrom(&atom_cache_
);
331 std::vector
< ::Atom
> requested_types
;
332 ui::GetAtomIntersection(url_atoms
, GetTargets(), &requested_types
);
334 if (requested_types
.empty())
337 // To actually answer whether we have a file, we need to look through the
338 // contents of the kMimeTypeURIList type, and see if any of them are file://
340 ui::SelectionData
data(format_map_
.GetFirstOf(requested_types
));
341 if (data
.IsValid()) {
342 std::vector
<std::string
> tokens
= ui::ParseURIList(data
);
343 for (std::vector
<std::string
>::const_iterator it
= tokens
.begin();
344 it
!= tokens
.end(); ++it
) {
346 base::FilePath file_path
;
347 if (url
.SchemeIsFile() && net::FileURLToFilePath(url
, &file_path
))
355 bool OSExchangeDataProviderAuraX11::HasCustomFormat(
356 const OSExchangeData::CustomFormat
& format
) const {
357 std::vector
< ::Atom
> url_atoms
;
358 url_atoms
.push_back(atom_cache_
.GetAtom(format
.ToString().c_str()));
359 std::vector
< ::Atom
> requested_types
;
360 ui::GetAtomIntersection(url_atoms
, GetTargets(), &requested_types
);
362 return !requested_types
.empty();
365 void OSExchangeDataProviderAuraX11::SetHtml(const base::string16
& html
,
366 const GURL
& base_url
) {
367 std::vector
<unsigned char> bytes
;
368 // Manually jam a UTF16 BOM into bytes because otherwise, other programs will
370 bytes
.push_back(0xFF);
371 bytes
.push_back(0xFE);
372 ui::AddString16ToVector(html
, &bytes
);
373 scoped_refptr
<base::RefCountedMemory
> mem(
374 base::RefCountedBytes::TakeVector(&bytes
));
376 format_map_
.Insert(atom_cache_
.GetAtom(Clipboard::kMimeTypeHTML
), mem
);
379 bool OSExchangeDataProviderAuraX11::GetHtml(base::string16
* html
,
380 GURL
* base_url
) const {
381 std::vector
< ::Atom
> url_atoms
;
382 url_atoms
.push_back(atom_cache_
.GetAtom(Clipboard::kMimeTypeHTML
));
383 std::vector
< ::Atom
> requested_types
;
384 ui::GetAtomIntersection(url_atoms
, GetTargets(), &requested_types
);
386 ui::SelectionData
data(format_map_
.GetFirstOf(requested_types
));
387 if (data
.IsValid()) {
388 *html
= data
.GetHtml();
396 bool OSExchangeDataProviderAuraX11::HasHtml() const {
397 std::vector
< ::Atom
> url_atoms
;
398 url_atoms
.push_back(atom_cache_
.GetAtom(Clipboard::kMimeTypeHTML
));
399 std::vector
< ::Atom
> requested_types
;
400 ui::GetAtomIntersection(url_atoms
, GetTargets(), &requested_types
);
402 return !requested_types
.empty();
405 void OSExchangeDataProviderAuraX11::SetDragImage(
406 const gfx::ImageSkia
& image
,
407 const gfx::Vector2d
& cursor_offset
) {
409 drag_image_offset_
= cursor_offset
;
412 const gfx::ImageSkia
& OSExchangeDataProviderAuraX11::GetDragImage() const {
416 const gfx::Vector2d
& OSExchangeDataProviderAuraX11::GetDragImageOffset() const {
417 return drag_image_offset_
;
420 bool OSExchangeDataProviderAuraX11::Dispatch(const base::NativeEvent
& event
) {
423 case SelectionRequest
:
424 selection_owner_
.OnSelectionRequest(xev
->xselectionrequest
);
433 bool OSExchangeDataProviderAuraX11::GetPlainTextURL(GURL
* url
) const {
435 if (GetString(&text
)) {
437 if (test_url
.is_valid()) {
446 std::vector
< ::Atom
> OSExchangeDataProviderAuraX11::GetTargets() const {
447 return format_map_
.GetTypes();
450 ///////////////////////////////////////////////////////////////////////////////
451 // OSExchangeData, public:
454 OSExchangeData::Provider
* OSExchangeData::CreateProvider() {
455 return new OSExchangeDataProviderAuraX11();