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 {
179 // Various Linux file managers both pass a list of file:// URIs and set the
180 // string representation to the URI. We explicitly don't want to return use
181 // this representation.
185 std::vector< ::Atom> text_atoms = ui::GetTextAtomsFrom(&atom_cache_);
186 std::vector< ::Atom> requested_types;
187 ui::GetAtomIntersection(text_atoms, GetTargets(), &requested_types);
189 ui::SelectionData data(format_map_.GetFirstOf(requested_types));
190 if (data.IsValid()) {
191 std::string text = data.GetText();
192 *result = base::UTF8ToUTF16(text);
199 bool OSExchangeDataProviderAuraX11::GetURLAndTitle(
200 OSExchangeData::FilenameToURLPolicy policy,
202 base::string16* title) const {
203 std::vector< ::Atom> url_atoms = ui::GetURLAtomsFrom(&atom_cache_);
204 std::vector< ::Atom> requested_types;
205 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
207 ui::SelectionData data(format_map_.GetFirstOf(requested_types));
208 if (data.IsValid()) {
209 // TODO(erg): Technically, both of these forms can accept multiple URLs,
210 // but that doesn't match the assumptions of the rest of the system which
211 // expect single types.
213 if (data.GetType() == atom_cache_.GetAtom(kMimeTypeMozillaURL)) {
214 // Mozilla URLs are (UTF16: URL, newline, title).
215 base::string16 unparsed;
216 data.AssignTo(&unparsed);
218 std::vector<base::string16> tokens;
219 size_t num_tokens = Tokenize(unparsed, base::ASCIIToUTF16("\n"), &tokens);
220 if (num_tokens > 0) {
224 *title = base::string16();
226 *url = GURL(tokens[0]);
229 } else if (data.GetType() == atom_cache_.GetAtom(
230 Clipboard::kMimeTypeURIList)) {
231 std::vector<std::string> tokens = ui::ParseURIList(data);
232 for (std::vector<std::string>::const_iterator it = tokens.begin();
233 it != tokens.end(); ++it) {
235 if (!test_url.SchemeIsFile() ||
236 policy == OSExchangeData::CONVERT_FILENAMES) {
238 *title = base::string16();
248 bool OSExchangeDataProviderAuraX11::GetFilename(base::FilePath* path) const {
249 std::vector<OSExchangeData::FileInfo> filenames;
250 if (GetFilenames(&filenames)) {
251 *path = filenames.front().path;
258 bool OSExchangeDataProviderAuraX11::GetFilenames(
259 std::vector<OSExchangeData::FileInfo>* filenames) const {
260 std::vector< ::Atom> url_atoms = ui::GetURIListAtomsFrom(&atom_cache_);
261 std::vector< ::Atom> requested_types;
262 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
265 ui::SelectionData data(format_map_.GetFirstOf(requested_types));
266 if (data.IsValid()) {
267 std::vector<std::string> tokens = ui::ParseURIList(data);
268 for (std::vector<std::string>::const_iterator it = tokens.begin();
269 it != tokens.end(); ++it) {
271 base::FilePath file_path;
272 if (url.SchemeIsFile() && net::FileURLToFilePath(url, &file_path)) {
273 filenames->push_back(OSExchangeData::FileInfo(file_path,
279 return !filenames->empty();
282 bool OSExchangeDataProviderAuraX11::GetPickledData(
283 const OSExchangeData::CustomFormat& format,
284 Pickle* pickle) const {
285 std::vector< ::Atom> requested_types;
286 requested_types.push_back(atom_cache_.GetAtom(format.ToString().c_str()));
288 ui::SelectionData data(format_map_.GetFirstOf(requested_types));
289 if (data.IsValid()) {
290 // Note that the pickle object on the right hand side of the assignment
291 // only refers to the bytes in |data|. The assignment copies the data.
292 *pickle = Pickle(reinterpret_cast<const char*>(data.GetData()),
293 static_cast<int>(data.GetSize()));
300 bool OSExchangeDataProviderAuraX11::HasString() const {
301 std::vector< ::Atom> text_atoms = ui::GetTextAtomsFrom(&atom_cache_);
302 std::vector< ::Atom> requested_types;
303 ui::GetAtomIntersection(text_atoms, GetTargets(), &requested_types);
304 return !requested_types.empty() && !HasFile();
307 bool OSExchangeDataProviderAuraX11::HasURL() const {
308 std::vector< ::Atom> url_atoms = ui::GetURLAtomsFrom(&atom_cache_);
309 std::vector< ::Atom> requested_types;
310 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
312 if (requested_types.empty())
315 // The Linux desktop doesn't differentiate between files and URLs like
316 // Windows does and stuffs all the data into one mime type.
317 ui::SelectionData data(format_map_.GetFirstOf(requested_types));
318 if (data.IsValid()) {
319 if (data.GetType() == atom_cache_.GetAtom(kMimeTypeMozillaURL)) {
320 // File managers shouldn't be using this type, so this is a URL.
322 } else if (data.GetType() == atom_cache_.GetAtom(
323 ui::Clipboard::kMimeTypeURIList)) {
324 std::vector<std::string> tokens = ui::ParseURIList(data);
325 for (std::vector<std::string>::const_iterator it = tokens.begin();
326 it != tokens.end(); ++it) {
327 if (!GURL(*it).SchemeIsFile())
338 bool OSExchangeDataProviderAuraX11::HasFile() const {
339 std::vector< ::Atom> url_atoms = ui::GetURIListAtomsFrom(&atom_cache_);
340 std::vector< ::Atom> requested_types;
341 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
343 if (requested_types.empty())
346 // To actually answer whether we have a file, we need to look through the
347 // contents of the kMimeTypeURIList type, and see if any of them are file://
349 ui::SelectionData data(format_map_.GetFirstOf(requested_types));
350 if (data.IsValid()) {
351 std::vector<std::string> tokens = ui::ParseURIList(data);
352 for (std::vector<std::string>::const_iterator it = tokens.begin();
353 it != tokens.end(); ++it) {
355 base::FilePath file_path;
356 if (url.SchemeIsFile() && net::FileURLToFilePath(url, &file_path))
364 bool OSExchangeDataProviderAuraX11::HasCustomFormat(
365 const OSExchangeData::CustomFormat& format) const {
366 std::vector< ::Atom> url_atoms;
367 url_atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str()));
368 std::vector< ::Atom> requested_types;
369 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
371 return !requested_types.empty();
374 void OSExchangeDataProviderAuraX11::SetHtml(const base::string16& html,
375 const GURL& base_url) {
376 std::vector<unsigned char> bytes;
377 // Manually jam a UTF16 BOM into bytes because otherwise, other programs will
379 bytes.push_back(0xFF);
380 bytes.push_back(0xFE);
381 ui::AddString16ToVector(html, &bytes);
382 scoped_refptr<base::RefCountedMemory> mem(
383 base::RefCountedBytes::TakeVector(&bytes));
385 format_map_.Insert(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML), mem);
388 bool OSExchangeDataProviderAuraX11::GetHtml(base::string16* html,
389 GURL* base_url) const {
390 std::vector< ::Atom> url_atoms;
391 url_atoms.push_back(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML));
392 std::vector< ::Atom> requested_types;
393 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
395 ui::SelectionData data(format_map_.GetFirstOf(requested_types));
396 if (data.IsValid()) {
397 *html = data.GetHtml();
405 bool OSExchangeDataProviderAuraX11::HasHtml() const {
406 std::vector< ::Atom> url_atoms;
407 url_atoms.push_back(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML));
408 std::vector< ::Atom> requested_types;
409 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
411 return !requested_types.empty();
414 void OSExchangeDataProviderAuraX11::SetDragImage(
415 const gfx::ImageSkia& image,
416 const gfx::Vector2d& cursor_offset) {
418 drag_image_offset_ = cursor_offset;
421 const gfx::ImageSkia& OSExchangeDataProviderAuraX11::GetDragImage() const {
425 const gfx::Vector2d& OSExchangeDataProviderAuraX11::GetDragImageOffset() const {
426 return drag_image_offset_;
429 uint32_t OSExchangeDataProviderAuraX11::Dispatch(
430 const base::NativeEvent& event) {
433 case SelectionRequest:
434 selection_owner_.OnSelectionRequest(xev->xselectionrequest);
440 return POST_DISPATCH_NONE;
443 bool OSExchangeDataProviderAuraX11::GetPlainTextURL(GURL* url) const {
445 if (GetString(&text)) {
447 if (test_url.is_valid()) {
456 std::vector< ::Atom> OSExchangeDataProviderAuraX11::GetTargets() const {
457 return format_map_.GetTypes();
460 ///////////////////////////////////////////////////////////////////////////////
461 // OSExchangeData, public:
464 OSExchangeData::Provider* OSExchangeData::CreateProvider() {
465 return new OSExchangeDataProviderAuraX11();