| OLD | NEW |
| (Empty) |
| 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/clipboard/clipboard_aurax11.h" | |
| 6 | |
| 7 #include <X11/extensions/Xfixes.h> | |
| 8 #include <X11/Xatom.h> | |
| 9 #include <list> | |
| 10 #include <set> | |
| 11 | |
| 12 #include "base/basictypes.h" | |
| 13 #include "base/files/file_path.h" | |
| 14 #include "base/logging.h" | |
| 15 #include "base/memory/ref_counted_memory.h" | |
| 16 #include "base/memory/scoped_ptr.h" | |
| 17 #include "base/memory/singleton.h" | |
| 18 #include "base/metrics/histogram.h" | |
| 19 #include "base/stl_util.h" | |
| 20 #include "base/strings/utf_string_conversions.h" | |
| 21 #include "third_party/skia/include/core/SkBitmap.h" | |
| 22 #include "ui/base/clipboard/custom_data_helper.h" | |
| 23 #include "ui/base/x/selection_owner.h" | |
| 24 #include "ui/base/x/selection_requestor.h" | |
| 25 #include "ui/base/x/selection_utils.h" | |
| 26 #include "ui/base/x/x11_util.h" | |
| 27 #include "ui/events/platform/platform_event_dispatcher.h" | |
| 28 #include "ui/events/platform/platform_event_observer.h" | |
| 29 #include "ui/events/platform/platform_event_source.h" | |
| 30 #include "ui/gfx/codec/png_codec.h" | |
| 31 #include "ui/gfx/size.h" | |
| 32 #include "ui/gfx/x/x11_atom_cache.h" | |
| 33 | |
| 34 namespace ui { | |
| 35 | |
| 36 namespace { | |
| 37 | |
| 38 const char kClipboard[] = "CLIPBOARD"; | |
| 39 const char kClipboardManager[] = "CLIPBOARD_MANAGER"; | |
| 40 const char kMimeTypeFilename[] = "chromium/filename"; | |
| 41 const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data"; | |
| 42 const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste"; | |
| 43 const char kSaveTargets[] = "SAVE_TARGETS"; | |
| 44 const char kTargets[] = "TARGETS"; | |
| 45 | |
| 46 const char* kAtomsToCache[] = { | |
| 47 kClipboard, | |
| 48 kClipboardManager, | |
| 49 Clipboard::kMimeTypePNG, | |
| 50 kMimeTypeFilename, | |
| 51 kMimeTypeMozillaURL, | |
| 52 kMimeTypeWebkitSmartPaste, | |
| 53 kSaveTargets, | |
| 54 kString, | |
| 55 kTargets, | |
| 56 kText, | |
| 57 kUtf8String, | |
| 58 NULL | |
| 59 }; | |
| 60 | |
| 61 /////////////////////////////////////////////////////////////////////////////// | |
| 62 | |
| 63 // Uses the XFixes API to provide sequence numbers for GetSequenceNumber(). | |
| 64 class SelectionChangeObserver : public ui::PlatformEventObserver { | |
| 65 public: | |
| 66 static SelectionChangeObserver* GetInstance(); | |
| 67 | |
| 68 uint64 clipboard_sequence_number() const { | |
| 69 return clipboard_sequence_number_; | |
| 70 } | |
| 71 uint64 primary_sequence_number() const { return primary_sequence_number_; } | |
| 72 | |
| 73 private: | |
| 74 friend struct DefaultSingletonTraits<SelectionChangeObserver>; | |
| 75 | |
| 76 SelectionChangeObserver(); | |
| 77 virtual ~SelectionChangeObserver(); | |
| 78 | |
| 79 // ui::PlatformEventObserver: | |
| 80 virtual void WillProcessEvent(const ui::PlatformEvent& event) override; | |
| 81 virtual void DidProcessEvent(const ui::PlatformEvent& event) override {} | |
| 82 | |
| 83 int event_base_; | |
| 84 Atom clipboard_atom_; | |
| 85 uint64 clipboard_sequence_number_; | |
| 86 uint64 primary_sequence_number_; | |
| 87 | |
| 88 DISALLOW_COPY_AND_ASSIGN(SelectionChangeObserver); | |
| 89 }; | |
| 90 | |
| 91 SelectionChangeObserver::SelectionChangeObserver() | |
| 92 : event_base_(-1), | |
| 93 clipboard_atom_(None), | |
| 94 clipboard_sequence_number_(0), | |
| 95 primary_sequence_number_(0) { | |
| 96 int ignored; | |
| 97 if (XFixesQueryExtension(gfx::GetXDisplay(), &event_base_, &ignored)) { | |
| 98 clipboard_atom_ = XInternAtom(gfx::GetXDisplay(), kClipboard, false); | |
| 99 XFixesSelectSelectionInput(gfx::GetXDisplay(), GetX11RootWindow(), | |
| 100 clipboard_atom_, | |
| 101 XFixesSetSelectionOwnerNotifyMask | | |
| 102 XFixesSelectionWindowDestroyNotifyMask | | |
| 103 XFixesSelectionClientCloseNotifyMask); | |
| 104 // This seems to be semi-optional. For some reason, registering for any | |
| 105 // selection notify events seems to subscribe us to events for both the | |
| 106 // primary and the clipboard buffers. Register anyway just to be safe. | |
| 107 XFixesSelectSelectionInput(gfx::GetXDisplay(), GetX11RootWindow(), | |
| 108 XA_PRIMARY, | |
| 109 XFixesSetSelectionOwnerNotifyMask | | |
| 110 XFixesSelectionWindowDestroyNotifyMask | | |
| 111 XFixesSelectionClientCloseNotifyMask); | |
| 112 | |
| 113 ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this); | |
| 114 } | |
| 115 } | |
| 116 | |
| 117 SelectionChangeObserver::~SelectionChangeObserver() { | |
| 118 // We are a singleton; we will outlive the event source. | |
| 119 } | |
| 120 | |
| 121 SelectionChangeObserver* SelectionChangeObserver::GetInstance() { | |
| 122 return Singleton<SelectionChangeObserver>::get(); | |
| 123 } | |
| 124 | |
| 125 void SelectionChangeObserver::WillProcessEvent(const ui::PlatformEvent& event) { | |
| 126 if (event->type == event_base_ + XFixesSelectionNotify) { | |
| 127 XFixesSelectionNotifyEvent* ev = | |
| 128 reinterpret_cast<XFixesSelectionNotifyEvent*>(event); | |
| 129 if (ev->selection == clipboard_atom_) { | |
| 130 clipboard_sequence_number_++; | |
| 131 } else if (ev->selection == XA_PRIMARY) { | |
| 132 primary_sequence_number_++; | |
| 133 } else { | |
| 134 DLOG(ERROR) << "Unexpected selection atom: " << ev->selection; | |
| 135 } | |
| 136 } | |
| 137 } | |
| 138 | |
| 139 /////////////////////////////////////////////////////////////////////////////// | |
| 140 | |
| 141 // Represents a list of possible return types. Copy constructable. | |
| 142 class TargetList { | |
| 143 public: | |
| 144 typedef std::vector< ::Atom> AtomVector; | |
| 145 | |
| 146 TargetList(const AtomVector& target_list, X11AtomCache* atom_cache); | |
| 147 | |
| 148 const AtomVector& target_list() { return target_list_; } | |
| 149 | |
| 150 bool ContainsText() const; | |
| 151 bool ContainsFormat(const Clipboard::FormatType& format_type) const; | |
| 152 bool ContainsAtom(::Atom atom) const; | |
| 153 | |
| 154 private: | |
| 155 AtomVector target_list_; | |
| 156 X11AtomCache* atom_cache_; | |
| 157 }; | |
| 158 | |
| 159 TargetList::TargetList(const AtomVector& target_list, | |
| 160 X11AtomCache* atom_cache) | |
| 161 : target_list_(target_list), | |
| 162 atom_cache_(atom_cache) { | |
| 163 } | |
| 164 | |
| 165 bool TargetList::ContainsText() const { | |
| 166 std::vector< ::Atom> atoms = GetTextAtomsFrom(atom_cache_); | |
| 167 for (std::vector< ::Atom>::const_iterator it = atoms.begin(); | |
| 168 it != atoms.end(); ++it) { | |
| 169 if (ContainsAtom(*it)) | |
| 170 return true; | |
| 171 } | |
| 172 | |
| 173 return false; | |
| 174 } | |
| 175 | |
| 176 bool TargetList::ContainsFormat( | |
| 177 const Clipboard::FormatType& format_type) const { | |
| 178 ::Atom atom = atom_cache_->GetAtom(format_type.ToString().c_str()); | |
| 179 return ContainsAtom(atom); | |
| 180 } | |
| 181 | |
| 182 bool TargetList::ContainsAtom(::Atom atom) const { | |
| 183 return find(target_list_.begin(), target_list_.end(), atom) | |
| 184 != target_list_.end(); | |
| 185 } | |
| 186 | |
| 187 } // namespace | |
| 188 | |
| 189 /////////////////////////////////////////////////////////////////////////////// | |
| 190 | |
| 191 // I would love for the FormatType to really be a wrapper around an X11 ::Atom, | |
| 192 // but there are a few problems. Chromeos unit tests spawn a new X11 server for | |
| 193 // each test, so Atom numeric values don't persist across tests. We could still | |
| 194 // maybe deal with that if we didn't have static accessor methods everywhere. | |
| 195 | |
| 196 Clipboard::FormatType::FormatType() { | |
| 197 } | |
| 198 | |
| 199 Clipboard::FormatType::FormatType(const std::string& native_format) | |
| 200 : data_(native_format) { | |
| 201 } | |
| 202 | |
| 203 Clipboard::FormatType::~FormatType() { | |
| 204 } | |
| 205 | |
| 206 std::string Clipboard::FormatType::Serialize() const { | |
| 207 return data_; | |
| 208 } | |
| 209 | |
| 210 // static | |
| 211 Clipboard::FormatType Clipboard::FormatType::Deserialize( | |
| 212 const std::string& serialization) { | |
| 213 return FormatType(serialization); | |
| 214 } | |
| 215 | |
| 216 bool Clipboard::FormatType::operator<(const FormatType& other) const { | |
| 217 return data_ < other.data_; | |
| 218 } | |
| 219 | |
| 220 bool Clipboard::FormatType::Equals(const FormatType& other) const { | |
| 221 return data_ == other.data_; | |
| 222 } | |
| 223 | |
| 224 /////////////////////////////////////////////////////////////////////////////// | |
| 225 // ClipboardAuraX11::AuraX11Details | |
| 226 | |
| 227 // Private implementation of our X11 integration. Keeps X11 headers out of the | |
| 228 // majority of chrome, which break badly. | |
| 229 class ClipboardAuraX11::AuraX11Details : public PlatformEventDispatcher { | |
| 230 public: | |
| 231 AuraX11Details(); | |
| 232 virtual ~AuraX11Details(); | |
| 233 | |
| 234 X11AtomCache* atom_cache() { return &atom_cache_; } | |
| 235 | |
| 236 // Returns the X11 type that we pass to various XSelection functions for the | |
| 237 // given type. | |
| 238 ::Atom LookupSelectionForClipboardType(ClipboardType type) const; | |
| 239 | |
| 240 // Returns the X11 type that we pass to various XSelection functions for | |
| 241 // CLIPBOARD_TYPE_COPY_PASTE. | |
| 242 ::Atom GetCopyPasteSelection() const; | |
| 243 | |
| 244 // Finds the SelectionFormatMap for the incoming selection atom. | |
| 245 const SelectionFormatMap& LookupStorageForAtom(::Atom atom); | |
| 246 | |
| 247 // As we need to collect all the data types before we tell X11 that we own a | |
| 248 // particular selection, we create a temporary clipboard mapping that | |
| 249 // InsertMapping writes to. Then we commit it in TakeOwnershipOfSelection, | |
| 250 // where we save it in one of the clipboard data slots. | |
| 251 void CreateNewClipboardData(); | |
| 252 | |
| 253 // Inserts a mapping into clipboard_data_. | |
| 254 void InsertMapping(const std::string& key, | |
| 255 const scoped_refptr<base::RefCountedMemory>& memory); | |
| 256 | |
| 257 // Moves the temporary |clipboard_data_| to the long term data storage for | |
| 258 // |type|. | |
| 259 void TakeOwnershipOfSelection(ClipboardType type); | |
| 260 | |
| 261 // Returns the first of |types| offered by the current selection holder in | |
| 262 // |data_out|, or returns NULL if none of those types are available. | |
| 263 // | |
| 264 // If the selection holder is us, this call is synchronous and we pull | |
| 265 // the data out of |clipboard_selection_| or |primary_selection_|. If the | |
| 266 // selection holder is some other window, we spin up a nested message loop | |
| 267 // and do the asynchronous dance with whatever application is holding the | |
| 268 // selection. | |
| 269 ui::SelectionData RequestAndWaitForTypes(ClipboardType type, | |
| 270 const std::vector< ::Atom>& types); | |
| 271 | |
| 272 // Retrieves the list of possible data types the current clipboard owner has. | |
| 273 // | |
| 274 // If the selection holder is us, this is synchronous, otherwise this runs a | |
| 275 // blocking message loop. | |
| 276 TargetList WaitAndGetTargetsList(ClipboardType type); | |
| 277 | |
| 278 // Returns a list of all text atoms that we handle. | |
| 279 std::vector< ::Atom> GetTextAtoms() const; | |
| 280 | |
| 281 // Returns a vector with a |format| converted to an X11 atom. | |
| 282 std::vector< ::Atom> GetAtomsForFormat(const Clipboard::FormatType& format); | |
| 283 | |
| 284 // Clears a certain clipboard type, whether we own it or not. | |
| 285 void Clear(ClipboardType type); | |
| 286 | |
| 287 // If we own the CLIPBOARD selection, requests the clipboard manager to take | |
| 288 // ownership of it. | |
| 289 void StoreCopyPasteDataAndWait(); | |
| 290 | |
| 291 private: | |
| 292 // PlatformEventDispatcher: | |
| 293 virtual bool CanDispatchEvent(const PlatformEvent& event) override; | |
| 294 virtual uint32_t DispatchEvent(const PlatformEvent& event) override; | |
| 295 | |
| 296 // Our X11 state. | |
| 297 Display* x_display_; | |
| 298 ::Window x_root_window_; | |
| 299 | |
| 300 // Input-only window used as a selection owner. | |
| 301 ::Window x_window_; | |
| 302 | |
| 303 X11AtomCache atom_cache_; | |
| 304 | |
| 305 // Object which requests and receives selection data. | |
| 306 SelectionRequestor selection_requestor_; | |
| 307 | |
| 308 // Temporary target map that we write to during DispatchObects. | |
| 309 SelectionFormatMap clipboard_data_; | |
| 310 | |
| 311 // Objects which offer selection data to other windows. | |
| 312 SelectionOwner clipboard_owner_; | |
| 313 SelectionOwner primary_owner_; | |
| 314 | |
| 315 DISALLOW_COPY_AND_ASSIGN(AuraX11Details); | |
| 316 }; | |
| 317 | |
| 318 ClipboardAuraX11::AuraX11Details::AuraX11Details() | |
| 319 : x_display_(gfx::GetXDisplay()), | |
| 320 x_root_window_(DefaultRootWindow(x_display_)), | |
| 321 x_window_(XCreateWindow(x_display_, | |
| 322 x_root_window_, | |
| 323 -100, | |
| 324 -100, | |
| 325 10, | |
| 326 10, // x, y, width, height | |
| 327 0, // border width | |
| 328 CopyFromParent, // depth | |
| 329 InputOnly, | |
| 330 CopyFromParent, // visual | |
| 331 0, | |
| 332 NULL)), | |
| 333 atom_cache_(x_display_, kAtomsToCache), | |
| 334 selection_requestor_(x_display_, x_window_, this), | |
| 335 clipboard_owner_(x_display_, x_window_, atom_cache_.GetAtom(kClipboard)), | |
| 336 primary_owner_(x_display_, x_window_, XA_PRIMARY) { | |
| 337 // We don't know all possible MIME types at compile time. | |
| 338 atom_cache_.allow_uncached_atoms(); | |
| 339 | |
| 340 XStoreName(x_display_, x_window_, "Chromium clipboard"); | |
| 341 XSelectInput(x_display_, x_window_, PropertyChangeMask); | |
| 342 | |
| 343 if (PlatformEventSource::GetInstance()) | |
| 344 PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); | |
| 345 } | |
| 346 | |
| 347 ClipboardAuraX11::AuraX11Details::~AuraX11Details() { | |
| 348 if (PlatformEventSource::GetInstance()) | |
| 349 PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); | |
| 350 | |
| 351 XDestroyWindow(x_display_, x_window_); | |
| 352 } | |
| 353 | |
| 354 ::Atom ClipboardAuraX11::AuraX11Details::LookupSelectionForClipboardType( | |
| 355 ClipboardType type) const { | |
| 356 if (type == CLIPBOARD_TYPE_COPY_PASTE) | |
| 357 return GetCopyPasteSelection(); | |
| 358 | |
| 359 return XA_PRIMARY; | |
| 360 } | |
| 361 | |
| 362 ::Atom ClipboardAuraX11::AuraX11Details::GetCopyPasteSelection() const { | |
| 363 return atom_cache_.GetAtom(kClipboard); | |
| 364 } | |
| 365 | |
| 366 const SelectionFormatMap& | |
| 367 ClipboardAuraX11::AuraX11Details::LookupStorageForAtom(::Atom atom) { | |
| 368 if (atom == XA_PRIMARY) | |
| 369 return primary_owner_.selection_format_map(); | |
| 370 | |
| 371 DCHECK_EQ(GetCopyPasteSelection(), atom); | |
| 372 return clipboard_owner_.selection_format_map(); | |
| 373 } | |
| 374 | |
| 375 void ClipboardAuraX11::AuraX11Details::CreateNewClipboardData() { | |
| 376 clipboard_data_ = SelectionFormatMap(); | |
| 377 } | |
| 378 | |
| 379 void ClipboardAuraX11::AuraX11Details::InsertMapping( | |
| 380 const std::string& key, | |
| 381 const scoped_refptr<base::RefCountedMemory>& memory) { | |
| 382 ::Atom atom_key = atom_cache_.GetAtom(key.c_str()); | |
| 383 clipboard_data_.Insert(atom_key, memory); | |
| 384 } | |
| 385 | |
| 386 void ClipboardAuraX11::AuraX11Details::TakeOwnershipOfSelection( | |
| 387 ClipboardType type) { | |
| 388 if (type == CLIPBOARD_TYPE_COPY_PASTE) | |
| 389 return clipboard_owner_.TakeOwnershipOfSelection(clipboard_data_); | |
| 390 else | |
| 391 return primary_owner_.TakeOwnershipOfSelection(clipboard_data_); | |
| 392 } | |
| 393 | |
| 394 SelectionData ClipboardAuraX11::AuraX11Details::RequestAndWaitForTypes( | |
| 395 ClipboardType type, | |
| 396 const std::vector<::Atom>& types) { | |
| 397 ::Atom selection_name = LookupSelectionForClipboardType(type); | |
| 398 if (XGetSelectionOwner(x_display_, selection_name) == x_window_) { | |
| 399 // We can local fastpath instead of playing the nested message loop game | |
| 400 // with the X server. | |
| 401 const SelectionFormatMap& format_map = LookupStorageForAtom(selection_name); | |
| 402 | |
| 403 for (std::vector< ::Atom>::const_iterator it = types.begin(); | |
| 404 it != types.end(); ++it) { | |
| 405 SelectionFormatMap::const_iterator format_map_it = format_map.find(*it); | |
| 406 if (format_map_it != format_map.end()) | |
| 407 return SelectionData(format_map_it->first, format_map_it->second); | |
| 408 } | |
| 409 } else { | |
| 410 TargetList targets = WaitAndGetTargetsList(type); | |
| 411 | |
| 412 ::Atom selection_name = LookupSelectionForClipboardType(type); | |
| 413 std::vector< ::Atom> intersection; | |
| 414 ui::GetAtomIntersection(types, targets.target_list(), &intersection); | |
| 415 return selection_requestor_.RequestAndWaitForTypes(selection_name, | |
| 416 intersection); | |
| 417 } | |
| 418 | |
| 419 return SelectionData(); | |
| 420 } | |
| 421 | |
| 422 TargetList ClipboardAuraX11::AuraX11Details::WaitAndGetTargetsList( | |
| 423 ClipboardType type) { | |
| 424 ::Atom selection_name = LookupSelectionForClipboardType(type); | |
| 425 std::vector< ::Atom> out; | |
| 426 if (XGetSelectionOwner(x_display_, selection_name) == x_window_) { | |
| 427 // We can local fastpath and return the list of local targets. | |
| 428 const SelectionFormatMap& format_map = LookupStorageForAtom(selection_name); | |
| 429 | |
| 430 for (SelectionFormatMap::const_iterator it = format_map.begin(); | |
| 431 it != format_map.end(); ++it) { | |
| 432 out.push_back(it->first); | |
| 433 } | |
| 434 } else { | |
| 435 scoped_refptr<base::RefCountedMemory> data; | |
| 436 size_t out_data_items = 0; | |
| 437 ::Atom out_type = None; | |
| 438 | |
| 439 if (selection_requestor_.PerformBlockingConvertSelection( | |
| 440 selection_name, | |
| 441 atom_cache_.GetAtom(kTargets), | |
| 442 &data, | |
| 443 &out_data_items, | |
| 444 &out_type)) { | |
| 445 // Some apps return an |out_type| of "TARGETS". (crbug.com/377893) | |
| 446 if (out_type == XA_ATOM || out_type == atom_cache_.GetAtom(kTargets)) { | |
| 447 const ::Atom* atom_array = | |
| 448 reinterpret_cast<const ::Atom*>(data->front()); | |
| 449 for (size_t i = 0; i < out_data_items; ++i) | |
| 450 out.push_back(atom_array[i]); | |
| 451 } | |
| 452 } else { | |
| 453 // There was no target list. Most Java apps doesn't offer a TARGETS list, | |
| 454 // even though they AWT to. They will offer individual text types if you | |
| 455 // ask. If this is the case we attempt to make sense of the contents as | |
| 456 // text. This is pretty unfortunate since it means we have to actually | |
| 457 // copy the data to see if it is available, but at least this path | |
| 458 // shouldn't be hit for conforming programs. | |
| 459 std::vector< ::Atom> types = GetTextAtoms(); | |
| 460 for (std::vector< ::Atom>::const_iterator it = types.begin(); | |
| 461 it != types.end(); ++it) { | |
| 462 ::Atom type = None; | |
| 463 if (selection_requestor_.PerformBlockingConvertSelection(selection_name, | |
| 464 *it, | |
| 465 NULL, | |
| 466 NULL, | |
| 467 &type) && | |
| 468 type == *it) { | |
| 469 out.push_back(*it); | |
| 470 } | |
| 471 } | |
| 472 } | |
| 473 } | |
| 474 | |
| 475 return TargetList(out, &atom_cache_); | |
| 476 } | |
| 477 | |
| 478 std::vector<::Atom> ClipboardAuraX11::AuraX11Details::GetTextAtoms() const { | |
| 479 return GetTextAtomsFrom(&atom_cache_); | |
| 480 } | |
| 481 | |
| 482 std::vector<::Atom> ClipboardAuraX11::AuraX11Details::GetAtomsForFormat( | |
| 483 const Clipboard::FormatType& format) { | |
| 484 std::vector< ::Atom> atoms; | |
| 485 atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str())); | |
| 486 return atoms; | |
| 487 } | |
| 488 | |
| 489 void ClipboardAuraX11::AuraX11Details::Clear(ClipboardType type) { | |
| 490 if (type == CLIPBOARD_TYPE_COPY_PASTE) | |
| 491 clipboard_owner_.ClearSelectionOwner(); | |
| 492 else | |
| 493 primary_owner_.ClearSelectionOwner(); | |
| 494 } | |
| 495 | |
| 496 void ClipboardAuraX11::AuraX11Details::StoreCopyPasteDataAndWait() { | |
| 497 ::Atom selection = GetCopyPasteSelection(); | |
| 498 if (XGetSelectionOwner(x_display_, selection) != x_window_) | |
| 499 return; | |
| 500 | |
| 501 ::Atom clipboard_manager_atom = atom_cache_.GetAtom(kClipboardManager); | |
| 502 if (XGetSelectionOwner(x_display_, clipboard_manager_atom) == None) | |
| 503 return; | |
| 504 | |
| 505 const SelectionFormatMap& format_map = LookupStorageForAtom(selection); | |
| 506 if (format_map.size() == 0) | |
| 507 return; | |
| 508 std::vector<Atom> targets = format_map.GetTypes(); | |
| 509 | |
| 510 base::TimeTicks start = base::TimeTicks::Now(); | |
| 511 selection_requestor_.PerformBlockingConvertSelectionWithParameter( | |
| 512 atom_cache_.GetAtom(kClipboardManager), | |
| 513 atom_cache_.GetAtom(kSaveTargets), | |
| 514 targets); | |
| 515 UMA_HISTOGRAM_TIMES("Clipboard.X11StoreCopyPasteDuration", | |
| 516 base::TimeTicks::Now() - start); | |
| 517 } | |
| 518 | |
| 519 bool ClipboardAuraX11::AuraX11Details::CanDispatchEvent( | |
| 520 const PlatformEvent& event) { | |
| 521 if (event->xany.window == x_window_) | |
| 522 return true; | |
| 523 | |
| 524 if (event->type == PropertyNotify) { | |
| 525 return primary_owner_.CanDispatchPropertyEvent(*event) || | |
| 526 clipboard_owner_.CanDispatchPropertyEvent(*event) || | |
| 527 selection_requestor_.CanDispatchPropertyEvent(*event); | |
| 528 } | |
| 529 return false; | |
| 530 } | |
| 531 | |
| 532 uint32_t ClipboardAuraX11::AuraX11Details::DispatchEvent( | |
| 533 const PlatformEvent& xev) { | |
| 534 switch (xev->type) { | |
| 535 case SelectionRequest: { | |
| 536 if (xev->xselectionrequest.selection == XA_PRIMARY) { | |
| 537 primary_owner_.OnSelectionRequest(*xev); | |
| 538 } else { | |
| 539 // We should not get requests for the CLIPBOARD_MANAGER selection | |
| 540 // because we never take ownership of it. | |
| 541 DCHECK_EQ(GetCopyPasteSelection(), xev->xselectionrequest.selection); | |
| 542 clipboard_owner_.OnSelectionRequest(*xev); | |
| 543 } | |
| 544 break; | |
| 545 } | |
| 546 case SelectionNotify: { | |
| 547 selection_requestor_.OnSelectionNotify(*xev); | |
| 548 break; | |
| 549 } | |
| 550 case SelectionClear: { | |
| 551 if (xev->xselectionclear.selection == XA_PRIMARY) { | |
| 552 primary_owner_.OnSelectionClear(*xev); | |
| 553 } else { | |
| 554 // We should not get requests for the CLIPBOARD_MANAGER selection | |
| 555 // because we never take ownership of it. | |
| 556 DCHECK_EQ(GetCopyPasteSelection(), xev->xselection.selection); | |
| 557 clipboard_owner_.OnSelectionClear(*xev); | |
| 558 } | |
| 559 break; | |
| 560 } | |
| 561 case PropertyNotify: { | |
| 562 if (primary_owner_.CanDispatchPropertyEvent(*xev)) | |
| 563 primary_owner_.OnPropertyEvent(*xev); | |
| 564 if (clipboard_owner_.CanDispatchPropertyEvent(*xev)) | |
| 565 clipboard_owner_.OnPropertyEvent(*xev); | |
| 566 if (selection_requestor_.CanDispatchPropertyEvent(*xev)) | |
| 567 selection_requestor_.OnPropertyEvent(*xev); | |
| 568 break; | |
| 569 } | |
| 570 default: | |
| 571 break; | |
| 572 } | |
| 573 | |
| 574 return POST_DISPATCH_NONE; | |
| 575 } | |
| 576 | |
| 577 /////////////////////////////////////////////////////////////////////////////// | |
| 578 // Various predefined FormatTypes. | |
| 579 // static | |
| 580 Clipboard::FormatType Clipboard::GetFormatType( | |
| 581 const std::string& format_string) { | |
| 582 return FormatType::Deserialize(format_string); | |
| 583 } | |
| 584 | |
| 585 // static | |
| 586 const Clipboard::FormatType& Clipboard::GetUrlFormatType() { | |
| 587 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeURIList)); | |
| 588 return type; | |
| 589 } | |
| 590 | |
| 591 // static | |
| 592 const Clipboard::FormatType& Clipboard::GetUrlWFormatType() { | |
| 593 return GetUrlFormatType(); | |
| 594 } | |
| 595 | |
| 596 // static | |
| 597 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { | |
| 598 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeText)); | |
| 599 return type; | |
| 600 } | |
| 601 | |
| 602 // static | |
| 603 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { | |
| 604 return GetPlainTextFormatType(); | |
| 605 } | |
| 606 | |
| 607 // static | |
| 608 const Clipboard::FormatType& Clipboard::GetFilenameFormatType() { | |
| 609 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeFilename)); | |
| 610 return type; | |
| 611 } | |
| 612 | |
| 613 // static | |
| 614 const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() { | |
| 615 return Clipboard::GetFilenameFormatType(); | |
| 616 } | |
| 617 | |
| 618 // static | |
| 619 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { | |
| 620 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeHTML)); | |
| 621 return type; | |
| 622 } | |
| 623 | |
| 624 // static | |
| 625 const Clipboard::FormatType& Clipboard::GetRtfFormatType() { | |
| 626 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeRTF)); | |
| 627 return type; | |
| 628 } | |
| 629 | |
| 630 // static | |
| 631 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { | |
| 632 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePNG)); | |
| 633 return type; | |
| 634 } | |
| 635 | |
| 636 // static | |
| 637 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { | |
| 638 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebkitSmartPaste)); | |
| 639 return type; | |
| 640 } | |
| 641 | |
| 642 // static | |
| 643 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { | |
| 644 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); | |
| 645 return type; | |
| 646 } | |
| 647 | |
| 648 // static | |
| 649 const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() { | |
| 650 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData)); | |
| 651 return type; | |
| 652 } | |
| 653 | |
| 654 /////////////////////////////////////////////////////////////////////////////// | |
| 655 // Clipboard factory method. | |
| 656 Clipboard* Clipboard::Create() { | |
| 657 return new ClipboardAuraX11; | |
| 658 } | |
| 659 | |
| 660 /////////////////////////////////////////////////////////////////////////////// | |
| 661 // ClipboardAuraX11 | |
| 662 | |
| 663 ClipboardAuraX11::ClipboardAuraX11() : aurax11_details_(new AuraX11Details) { | |
| 664 DCHECK(CalledOnValidThread()); | |
| 665 } | |
| 666 | |
| 667 ClipboardAuraX11::~ClipboardAuraX11() { | |
| 668 DCHECK(CalledOnValidThread()); | |
| 669 | |
| 670 aurax11_details_->StoreCopyPasteDataAndWait(); | |
| 671 } | |
| 672 | |
| 673 uint64 ClipboardAuraX11::GetSequenceNumber(ClipboardType type) { | |
| 674 DCHECK(CalledOnValidThread()); | |
| 675 if (type == CLIPBOARD_TYPE_COPY_PASTE) | |
| 676 return SelectionChangeObserver::GetInstance()->clipboard_sequence_number(); | |
| 677 else | |
| 678 return SelectionChangeObserver::GetInstance()->primary_sequence_number(); | |
| 679 } | |
| 680 | |
| 681 bool ClipboardAuraX11::IsFormatAvailable(const FormatType& format, | |
| 682 ClipboardType type) const { | |
| 683 DCHECK(CalledOnValidThread()); | |
| 684 DCHECK(IsSupportedClipboardType(type)); | |
| 685 | |
| 686 TargetList target_list = aurax11_details_->WaitAndGetTargetsList(type); | |
| 687 if (format.Equals(GetPlainTextFormatType()) || | |
| 688 format.Equals(GetUrlFormatType())) { | |
| 689 return target_list.ContainsText(); | |
| 690 } | |
| 691 return target_list.ContainsFormat(format); | |
| 692 } | |
| 693 | |
| 694 void ClipboardAuraX11::Clear(ClipboardType type) { | |
| 695 DCHECK(CalledOnValidThread()); | |
| 696 DCHECK(IsSupportedClipboardType(type)); | |
| 697 aurax11_details_->Clear(type); | |
| 698 } | |
| 699 | |
| 700 void ClipboardAuraX11::ReadAvailableTypes(ClipboardType type, | |
| 701 std::vector<base::string16>* types, | |
| 702 bool* contains_filenames) const { | |
| 703 DCHECK(CalledOnValidThread()); | |
| 704 if (!types || !contains_filenames) { | |
| 705 NOTREACHED(); | |
| 706 return; | |
| 707 } | |
| 708 | |
| 709 TargetList target_list = aurax11_details_->WaitAndGetTargetsList(type); | |
| 710 | |
| 711 types->clear(); | |
| 712 | |
| 713 if (target_list.ContainsText()) | |
| 714 types->push_back(base::UTF8ToUTF16(kMimeTypeText)); | |
| 715 if (target_list.ContainsFormat(GetHtmlFormatType())) | |
| 716 types->push_back(base::UTF8ToUTF16(kMimeTypeHTML)); | |
| 717 if (target_list.ContainsFormat(GetRtfFormatType())) | |
| 718 types->push_back(base::UTF8ToUTF16(kMimeTypeRTF)); | |
| 719 if (target_list.ContainsFormat(GetBitmapFormatType())) | |
| 720 types->push_back(base::UTF8ToUTF16(kMimeTypePNG)); | |
| 721 *contains_filenames = false; | |
| 722 | |
| 723 SelectionData data(aurax11_details_->RequestAndWaitForTypes( | |
| 724 type, aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType()))); | |
| 725 if (data.IsValid()) | |
| 726 ReadCustomDataTypes(data.GetData(), data.GetSize(), types); | |
| 727 } | |
| 728 | |
| 729 void ClipboardAuraX11::ReadText(ClipboardType type, | |
| 730 base::string16* result) const { | |
| 731 DCHECK(CalledOnValidThread()); | |
| 732 | |
| 733 SelectionData data(aurax11_details_->RequestAndWaitForTypes( | |
| 734 type, aurax11_details_->GetTextAtoms())); | |
| 735 if (data.IsValid()) { | |
| 736 std::string text = data.GetText(); | |
| 737 *result = base::UTF8ToUTF16(text); | |
| 738 } | |
| 739 } | |
| 740 | |
| 741 void ClipboardAuraX11::ReadAsciiText(ClipboardType type, | |
| 742 std::string* result) const { | |
| 743 DCHECK(CalledOnValidThread()); | |
| 744 | |
| 745 SelectionData data(aurax11_details_->RequestAndWaitForTypes( | |
| 746 type, aurax11_details_->GetTextAtoms())); | |
| 747 if (data.IsValid()) | |
| 748 result->assign(data.GetText()); | |
| 749 } | |
| 750 | |
| 751 // TODO(estade): handle different charsets. | |
| 752 // TODO(port): set *src_url. | |
| 753 void ClipboardAuraX11::ReadHTML(ClipboardType type, | |
| 754 base::string16* markup, | |
| 755 std::string* src_url, | |
| 756 uint32* fragment_start, | |
| 757 uint32* fragment_end) const { | |
| 758 DCHECK(CalledOnValidThread()); | |
| 759 markup->clear(); | |
| 760 if (src_url) | |
| 761 src_url->clear(); | |
| 762 *fragment_start = 0; | |
| 763 *fragment_end = 0; | |
| 764 | |
| 765 SelectionData data(aurax11_details_->RequestAndWaitForTypes( | |
| 766 type, aurax11_details_->GetAtomsForFormat(GetHtmlFormatType()))); | |
| 767 if (data.IsValid()) { | |
| 768 *markup = data.GetHtml(); | |
| 769 | |
| 770 *fragment_start = 0; | |
| 771 DCHECK(markup->length() <= kuint32max); | |
| 772 *fragment_end = static_cast<uint32>(markup->length()); | |
| 773 } | |
| 774 } | |
| 775 | |
| 776 void ClipboardAuraX11::ReadRTF(ClipboardType type, std::string* result) const { | |
| 777 DCHECK(CalledOnValidThread()); | |
| 778 | |
| 779 SelectionData data(aurax11_details_->RequestAndWaitForTypes( | |
| 780 type, aurax11_details_->GetAtomsForFormat(GetRtfFormatType()))); | |
| 781 if (data.IsValid()) | |
| 782 data.AssignTo(result); | |
| 783 } | |
| 784 | |
| 785 SkBitmap ClipboardAuraX11::ReadImage(ClipboardType type) const { | |
| 786 DCHECK(CalledOnValidThread()); | |
| 787 | |
| 788 SelectionData data(aurax11_details_->RequestAndWaitForTypes( | |
| 789 type, aurax11_details_->GetAtomsForFormat(GetBitmapFormatType()))); | |
| 790 if (data.IsValid()) { | |
| 791 SkBitmap bitmap; | |
| 792 if (gfx::PNGCodec::Decode(data.GetData(), data.GetSize(), &bitmap)) | |
| 793 return SkBitmap(bitmap); | |
| 794 } | |
| 795 | |
| 796 return SkBitmap(); | |
| 797 } | |
| 798 | |
| 799 void ClipboardAuraX11::ReadCustomData(ClipboardType clipboard_type, | |
| 800 const base::string16& type, | |
| 801 base::string16* result) const { | |
| 802 DCHECK(CalledOnValidThread()); | |
| 803 | |
| 804 SelectionData data(aurax11_details_->RequestAndWaitForTypes( | |
| 805 clipboard_type, | |
| 806 aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType()))); | |
| 807 if (data.IsValid()) | |
| 808 ReadCustomDataForType(data.GetData(), data.GetSize(), type, result); | |
| 809 } | |
| 810 | |
| 811 void ClipboardAuraX11::ReadBookmark(base::string16* title, | |
| 812 std::string* url) const { | |
| 813 DCHECK(CalledOnValidThread()); | |
| 814 // TODO(erg): This was left NOTIMPLEMENTED() in the gtk port too. | |
| 815 NOTIMPLEMENTED(); | |
| 816 } | |
| 817 | |
| 818 void ClipboardAuraX11::ReadData(const FormatType& format, | |
| 819 std::string* result) const { | |
| 820 DCHECK(CalledOnValidThread()); | |
| 821 | |
| 822 SelectionData data(aurax11_details_->RequestAndWaitForTypes( | |
| 823 CLIPBOARD_TYPE_COPY_PASTE, aurax11_details_->GetAtomsForFormat(format))); | |
| 824 if (data.IsValid()) | |
| 825 data.AssignTo(result); | |
| 826 } | |
| 827 | |
| 828 void ClipboardAuraX11::WriteObjects(ClipboardType type, | |
| 829 const ObjectMap& objects) { | |
| 830 DCHECK(CalledOnValidThread()); | |
| 831 DCHECK(IsSupportedClipboardType(type)); | |
| 832 | |
| 833 aurax11_details_->CreateNewClipboardData(); | |
| 834 for (ObjectMap::const_iterator iter = objects.begin(); iter != objects.end(); | |
| 835 ++iter) { | |
| 836 DispatchObject(static_cast<ObjectType>(iter->first), iter->second); | |
| 837 } | |
| 838 aurax11_details_->TakeOwnershipOfSelection(type); | |
| 839 | |
| 840 if (type == CLIPBOARD_TYPE_COPY_PASTE) { | |
| 841 ObjectMap::const_iterator text_iter = objects.find(CBF_TEXT); | |
| 842 if (text_iter != objects.end()) { | |
| 843 aurax11_details_->CreateNewClipboardData(); | |
| 844 const ObjectMapParams& params_vector = text_iter->second; | |
| 845 if (params_vector.size()) { | |
| 846 const ObjectMapParam& char_vector = params_vector[0]; | |
| 847 if (char_vector.size()) | |
| 848 WriteText(&char_vector.front(), char_vector.size()); | |
| 849 } | |
| 850 aurax11_details_->TakeOwnershipOfSelection(CLIPBOARD_TYPE_SELECTION); | |
| 851 } | |
| 852 } | |
| 853 } | |
| 854 | |
| 855 void ClipboardAuraX11::WriteText(const char* text_data, size_t text_len) { | |
| 856 std::string str(text_data, text_len); | |
| 857 scoped_refptr<base::RefCountedMemory> mem( | |
| 858 base::RefCountedString::TakeString(&str)); | |
| 859 | |
| 860 aurax11_details_->InsertMapping(kMimeTypeText, mem); | |
| 861 aurax11_details_->InsertMapping(kText, mem); | |
| 862 aurax11_details_->InsertMapping(kString, mem); | |
| 863 aurax11_details_->InsertMapping(kUtf8String, mem); | |
| 864 } | |
| 865 | |
| 866 void ClipboardAuraX11::WriteHTML(const char* markup_data, | |
| 867 size_t markup_len, | |
| 868 const char* url_data, | |
| 869 size_t url_len) { | |
| 870 // TODO(estade): We need to expand relative links with |url_data|. | |
| 871 static const char* html_prefix = "<meta http-equiv=\"content-type\" " | |
| 872 "content=\"text/html; charset=utf-8\">"; | |
| 873 std::string data = html_prefix; | |
| 874 data += std::string(markup_data, markup_len); | |
| 875 // Some programs expect NULL-terminated data. See http://crbug.com/42624 | |
| 876 data += '\0'; | |
| 877 | |
| 878 scoped_refptr<base::RefCountedMemory> mem( | |
| 879 base::RefCountedString::TakeString(&data)); | |
| 880 aurax11_details_->InsertMapping(kMimeTypeHTML, mem); | |
| 881 } | |
| 882 | |
| 883 void ClipboardAuraX11::WriteRTF(const char* rtf_data, size_t data_len) { | |
| 884 WriteData(GetRtfFormatType(), rtf_data, data_len); | |
| 885 } | |
| 886 | |
| 887 void ClipboardAuraX11::WriteBookmark(const char* title_data, | |
| 888 size_t title_len, | |
| 889 const char* url_data, | |
| 890 size_t url_len) { | |
| 891 // Write as a mozilla url (UTF16: URL, newline, title). | |
| 892 base::string16 url = base::UTF8ToUTF16(std::string(url_data, url_len) + "\n"); | |
| 893 base::string16 title = base::UTF8ToUTF16(std::string(title_data, title_len)); | |
| 894 | |
| 895 std::vector<unsigned char> data; | |
| 896 ui::AddString16ToVector(url, &data); | |
| 897 ui::AddString16ToVector(title, &data); | |
| 898 scoped_refptr<base::RefCountedMemory> mem( | |
| 899 base::RefCountedBytes::TakeVector(&data)); | |
| 900 | |
| 901 aurax11_details_->InsertMapping(kMimeTypeMozillaURL, mem); | |
| 902 } | |
| 903 | |
| 904 // Write an extra flavor that signifies WebKit was the last to modify the | |
| 905 // pasteboard. This flavor has no data. | |
| 906 void ClipboardAuraX11::WriteWebSmartPaste() { | |
| 907 std::string empty; | |
| 908 aurax11_details_->InsertMapping( | |
| 909 kMimeTypeWebkitSmartPaste, | |
| 910 scoped_refptr<base::RefCountedMemory>( | |
| 911 base::RefCountedString::TakeString(&empty))); | |
| 912 } | |
| 913 | |
| 914 void ClipboardAuraX11::WriteBitmap(const SkBitmap& bitmap) { | |
| 915 // Encode the bitmap as a PNG for transport. | |
| 916 std::vector<unsigned char> output; | |
| 917 if (gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, false, &output)) { | |
| 918 aurax11_details_->InsertMapping(kMimeTypePNG, | |
| 919 base::RefCountedBytes::TakeVector( | |
| 920 &output)); | |
| 921 } | |
| 922 } | |
| 923 | |
| 924 void ClipboardAuraX11::WriteData(const FormatType& format, | |
| 925 const char* data_data, | |
| 926 size_t data_len) { | |
| 927 // We assume that certain mapping types are only written by trusted code. | |
| 928 // Therefore we must upkeep their integrity. | |
| 929 if (format.Equals(GetBitmapFormatType())) | |
| 930 return; | |
| 931 | |
| 932 std::vector<unsigned char> bytes(data_data, data_data + data_len); | |
| 933 scoped_refptr<base::RefCountedMemory> mem( | |
| 934 base::RefCountedBytes::TakeVector(&bytes)); | |
| 935 aurax11_details_->InsertMapping(format.ToString(), mem); | |
| 936 } | |
| 937 | |
| 938 } // namespace ui | |
| OLD | NEW |