os_exchange_data_provider_aurax11.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/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" 17 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 21// information. 22 23namespace ui { 24 25namespace { 26 27const char kDndSelection[] = "XdndSelection"; 28 29const char* kAtomsToCache[] = { 30 kString, 31 kText, 32 kUtf8String, 33 kDndSelection, 34 Clipboard::kMimeTypeURIList, 35 kMimeTypeMozillaURL, 36 Clipboard::kMimeTypeText, 37 NULL 38}; 39 40} // namespace 41 42OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11( 43 ::Window x_window, 44 const SelectionFormatMap& selection) 45 : x_display_(gfx::GetXDisplay()), 46 x_root_window_(DefaultRootWindow(x_display_)), 47 own_window_(false), 48 x_window_(x_window), 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(); 55} 56 57OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11() 58 : x_display_(gfx::GetXDisplay()), 59 x_root_window_(DefaultRootWindow(x_display_)), 60 own_window_(true), 61 x_window_(XCreateWindow( 62 x_display_, 63 x_root_window_, 64 -100, -100, 10, 10, // x, y, width, height 65 0, // border width 66 CopyFromParent, // depth 67 InputOnly, 68 CopyFromParent, // visual 69 0, 70 NULL)), 71 atom_cache_(x_display_, kAtomsToCache), 72 format_map_(), 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(); 77 78 XStoreName(x_display_, x_window_, "Chromium Drag & Drop Window"); 79 80 base::MessagePumpX11::Current()->AddDispatcherForWindow(this, x_window_); 81} 82 83OSExchangeDataProviderAuraX11::~OSExchangeDataProviderAuraX11() { 84 if (own_window_) { 85 base::MessagePumpX11::Current()->RemoveDispatcherForWindow(x_window_); 86 XDestroyWindow(x_display_, x_window_); 87 } 88} 89 90void OSExchangeDataProviderAuraX11::TakeOwnershipOfSelection() const { 91 selection_owner_.TakeOwnershipOfSelection(format_map_); 92} 93 94void OSExchangeDataProviderAuraX11::RetrieveTargets( 95 std::vector<Atom>* targets) const { 96 selection_owner_.RetrieveTargets(targets); 97} 98 99SelectionFormatMap 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(); 103} 104 105OSExchangeData::Provider* OSExchangeDataProviderAuraX11::Clone() const { 106 OSExchangeDataProviderAuraX11* ret = new OSExchangeDataProviderAuraX11(); 107 ret->format_map_ = format_map_; 108 return ret; 109} 110 111void 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)); 115 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); 120} 121 122void 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()); 127 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)); 134 135 format_map_.Insert(atom_cache_.GetAtom(kMimeTypeMozillaURL), mem); 136 137 SetString(spec); 138 } 139} 140 141void OSExchangeDataProviderAuraX11::SetFilename(const base::FilePath& path) { 142 std::vector<OSExchangeData::FileInfo> data; 143 data.push_back(OSExchangeData::FileInfo(path, base::FilePath())); 144 SetFilenames(data); 145} 146 147void 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); 155 } 156 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); 161} 162 163void OSExchangeDataProviderAuraX11::SetPickledData( 164 const OSExchangeData::CustomFormat& format, 165 const Pickle& pickle) { 166 const unsigned char* data = 167 reinterpret_cast<const unsigned char*>(pickle.data()); 168 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)); 173 174 format_map_.Insert(atom_cache_.GetAtom(format.ToString().c_str()), mem); 175} 176 177bool OSExchangeDataProviderAuraX11::GetString(base::string16* result) const { 178 if (HasFile()) { 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. 182 return false; 183 } 184 185 std::vector< ::Atom> text_atoms = ui::GetTextAtomsFrom(&atom_cache_); 186 std::vector< ::Atom> requested_types; 187 ui::GetAtomIntersection(text_atoms, GetTargets(), &requested_types); 188 189 ui::SelectionData data(format_map_.GetFirstOf(requested_types)); 190 if (data.IsValid()) { 191 std::string text = data.GetText(); 192 *result = base::UTF8ToUTF16(text); 193 return true; 194 } 195 196 return false; 197} 198 199bool OSExchangeDataProviderAuraX11::GetURLAndTitle( 200 OSExchangeData::FilenameToURLPolicy policy, 201 GURL* url, 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); 206 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. 212 213 if (data.GetType() == atom_cache_.GetAtom(kMimeTypeMozillaURL)) { 214 // Mozilla URLs are (UTF16: URL, newline, title). 215 base::string16 unparsed; 216 data.AssignTo(&unparsed); 217 218 std::vector<base::string16> tokens; 219 size_t num_tokens = Tokenize(unparsed, base::ASCIIToUTF16("\n"), &tokens); 220 if (num_tokens > 0) { 221 if (num_tokens > 1) 222 *title = tokens[1]; 223 else 224 *title = base::string16(); 225 226 *url = GURL(tokens[0]); 227 return true; 228 } 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) { 234 GURL test_url(*it); 235 if (!test_url.SchemeIsFile() || 236 policy == OSExchangeData::CONVERT_FILENAMES) { 237 *url = test_url; 238 *title = base::string16(); 239 return true; 240 } 241 } 242 } 243 } 244 245 return false; 246} 247 248bool OSExchangeDataProviderAuraX11::GetFilename(base::FilePath* path) const { 249 std::vector<OSExchangeData::FileInfo> filenames; 250 if (GetFilenames(&filenames)) { 251 *path = filenames.front().path; 252 return true; 253 } 254 255 return false; 256} 257 258bool 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); 263 264 filenames->clear(); 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) { 270 GURL url(*it); 271 base::FilePath file_path; 272 if (url.SchemeIsFile() && net::FileURLToFilePath(url, &file_path)) { 273 filenames->push_back(OSExchangeData::FileInfo(file_path, 274 base::FilePath())); 275 } 276 } 277 } 278 279 return !filenames->empty(); 280} 281 282bool 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())); 287 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())); 294 return true; 295 } 296 297 return false; 298} 299 300bool 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(); 305} 306 307bool OSExchangeDataProviderAuraX11::HasURL( 308 OSExchangeData::FilenameToURLPolicy policy) const { 309 std::vector< ::Atom> url_atoms = ui::GetURLAtomsFrom(&atom_cache_); 310 std::vector< ::Atom> requested_types; 311 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types); 312 313 if (requested_types.empty()) 314 return false; 315 316 // The Linux desktop doesn't differentiate between files and URLs like 317 // Windows does and stuffs all the data into one mime type. 318 ui::SelectionData data(format_map_.GetFirstOf(requested_types)); 319 if (data.IsValid()) { 320 if (data.GetType() == atom_cache_.GetAtom(kMimeTypeMozillaURL)) { 321 // File managers shouldn't be using this type, so this is a URL. 322 return true; 323 } else if (data.GetType() == atom_cache_.GetAtom( 324 ui::Clipboard::kMimeTypeURIList)) { 325 std::vector<std::string> tokens = ui::ParseURIList(data); 326 for (std::vector<std::string>::const_iterator it = tokens.begin(); 327 it != tokens.end(); ++it) { 328 if (!GURL(*it).SchemeIsFile() || 329 policy == OSExchangeData::CONVERT_FILENAMES) 330 return true; 331 } 332 333 return false; 334 } 335 } 336 337 return false; 338} 339 340bool OSExchangeDataProviderAuraX11::HasFile() const { 341 std::vector< ::Atom> url_atoms = ui::GetURIListAtomsFrom(&atom_cache_); 342 std::vector< ::Atom> requested_types; 343 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types); 344 345 if (requested_types.empty()) 346 return false; 347 348 // To actually answer whether we have a file, we need to look through the 349 // contents of the kMimeTypeURIList type, and see if any of them are file:// 350 // URIs. 351 ui::SelectionData data(format_map_.GetFirstOf(requested_types)); 352 if (data.IsValid()) { 353 std::vector<std::string> tokens = ui::ParseURIList(data); 354 for (std::vector<std::string>::const_iterator it = tokens.begin(); 355 it != tokens.end(); ++it) { 356 GURL url(*it); 357 base::FilePath file_path; 358 if (url.SchemeIsFile() && net::FileURLToFilePath(url, &file_path)) 359 return true; 360 } 361 } 362 363 return false; 364} 365 366bool OSExchangeDataProviderAuraX11::HasCustomFormat( 367 const OSExchangeData::CustomFormat& format) const { 368 std::vector< ::Atom> url_atoms; 369 url_atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str())); 370 std::vector< ::Atom> requested_types; 371 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types); 372 373 return !requested_types.empty(); 374} 375 376void OSExchangeDataProviderAuraX11::SetHtml(const base::string16& html, 377 const GURL& base_url) { 378 std::vector<unsigned char> bytes; 379 // Manually jam a UTF16 BOM into bytes because otherwise, other programs will 380 // assume UTF-8. 381 bytes.push_back(0xFF); 382 bytes.push_back(0xFE); 383 ui::AddString16ToVector(html, &bytes); 384 scoped_refptr<base::RefCountedMemory> mem( 385 base::RefCountedBytes::TakeVector(&bytes)); 386 387 format_map_.Insert(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML), mem); 388} 389 390bool OSExchangeDataProviderAuraX11::GetHtml(base::string16* html, 391 GURL* base_url) const { 392 std::vector< ::Atom> url_atoms; 393 url_atoms.push_back(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML)); 394 std::vector< ::Atom> requested_types; 395 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types); 396 397 ui::SelectionData data(format_map_.GetFirstOf(requested_types)); 398 if (data.IsValid()) { 399 *html = data.GetHtml(); 400 *base_url = GURL(); 401 return true; 402 } 403 404 return false; 405} 406 407bool OSExchangeDataProviderAuraX11::HasHtml() const { 408 std::vector< ::Atom> url_atoms; 409 url_atoms.push_back(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML)); 410 std::vector< ::Atom> requested_types; 411 ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types); 412 413 return !requested_types.empty(); 414} 415 416void OSExchangeDataProviderAuraX11::SetDragImage( 417 const gfx::ImageSkia& image, 418 const gfx::Vector2d& cursor_offset) { 419 drag_image_ = image; 420 drag_image_offset_ = cursor_offset; 421} 422 423const gfx::ImageSkia& OSExchangeDataProviderAuraX11::GetDragImage() const { 424 return drag_image_; 425} 426 427const gfx::Vector2d& OSExchangeDataProviderAuraX11::GetDragImageOffset() const { 428 return drag_image_offset_; 429} 430 431uint32_t OSExchangeDataProviderAuraX11::Dispatch( 432 const base::NativeEvent& event) { 433 XEvent* xev = event; 434 switch (xev->type) { 435 case SelectionRequest: 436 selection_owner_.OnSelectionRequest(xev->xselectionrequest); 437 break; 438 default: 439 NOTIMPLEMENTED(); 440 } 441 442 return POST_DISPATCH_NONE; 443} 444 445bool OSExchangeDataProviderAuraX11::GetPlainTextURL(GURL* url) const { 446 base::string16 text; 447 if (GetString(&text)) { 448 GURL test_url(text); 449 if (test_url.is_valid()) { 450 *url = test_url; 451 return true; 452 } 453 } 454 455 return false; 456} 457 458std::vector< ::Atom> OSExchangeDataProviderAuraX11::GetTargets() const { 459 return format_map_.GetTypes(); 460} 461 462/////////////////////////////////////////////////////////////////////////////// 463// OSExchangeData, public: 464 465// static 466OSExchangeData::Provider* OSExchangeData::CreateProvider() { 467 return new OSExchangeDataProviderAuraX11(); 468} 469 470} // namespace ui 471