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.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 // Clipboard::AuraX11Details 226 227 // Private implementation of our X11 integration. Keeps X11 headers out of the 228 // majority of chrome, which break badly. 229 class Clipboard::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 // Returns the object which is responsible for communication on |type|. 245 SelectionRequestor* GetSelectionRequestorForClipboardType(ClipboardType type); 246 247 // Finds the SelectionFormatMap for the incoming selection atom. 248 const SelectionFormatMap& LookupStorageForAtom(::Atom atom); 249 250 // As we need to collect all the data types before we tell X11 that we own a 251 // particular selection, we create a temporary clipboard mapping that 252 // InsertMapping writes to. Then we commit it in TakeOwnershipOfSelection, 253 // where we save it in one of the clipboard data slots. 254 void CreateNewClipboardData(); 255 256 // Inserts a mapping into clipboard_data_. 257 void InsertMapping(const std::string& key, 258 const scoped_refptr<base::RefCountedMemory>& memory); 259 260 // Moves the temporary |clipboard_data_| to the long term data storage for 261 // |type|. 262 void TakeOwnershipOfSelection(ClipboardType type); 263 264 // Returns the first of |types| offered by the current selection holder in 265 // |data_out|, or returns NULL if none of those types are available. 266 // 267 // If the selection holder is us, this call is synchronous and we pull 268 // the data out of |clipboard_selection_| or |primary_selection_|. If the 269 // selection holder is some other window, we spin up a nested message loop 270 // and do the asynchronous dance with whatever application is holding the 271 // selection. 272 ui::SelectionData RequestAndWaitForTypes(ClipboardType type, 273 const std::vector< ::Atom>& types); 274 275 // Retrieves the list of possible data types the current clipboard owner has. 276 // 277 // If the selection holder is us, this is synchronous, otherwise this runs a 278 // blocking message loop. 279 TargetList WaitAndGetTargetsList(ClipboardType type); 280 281 // Returns a list of all text atoms that we handle. 282 std::vector< ::Atom> GetTextAtoms() const; 283 284 // Returns a vector with a |format| converted to an X11 atom. 285 std::vector< ::Atom> GetAtomsForFormat(const Clipboard::FormatType& format); 286 287 // Clears a certain clipboard type, whether we own it or not. 288 void Clear(ClipboardType type); 289 290 // If we own the CLIPBOARD selection, requests the clipboard manager to take 291 // ownership of it. 292 void StoreCopyPasteDataAndWait(); 293 294 private: 295 // PlatformEventDispatcher: 296 virtual bool CanDispatchEvent(const PlatformEvent& event) OVERRIDE; 297 virtual uint32_t DispatchEvent(const PlatformEvent& event) OVERRIDE; 298 299 // Our X11 state. 300 Display* x_display_; 301 ::Window x_root_window_; 302 303 // Input-only window used as a selection owner. 304 ::Window x_window_; 305 306 X11AtomCache atom_cache_; 307 308 // Objects which request and receive selection data. 309 SelectionRequestor clipboard_requestor_; 310 SelectionRequestor primary_requestor_; 311 SelectionRequestor clipboard_manager_requestor_; 312 313 // Temporary target map that we write to during DispatchObects. 314 SelectionFormatMap clipboard_data_; 315 316 // Objects which offer selection data to other windows. 317 SelectionOwner clipboard_owner_; 318 SelectionOwner primary_owner_; 319 320 DISALLOW_COPY_AND_ASSIGN(AuraX11Details); 321 }; 322 323 Clipboard::AuraX11Details::AuraX11Details() 324 : x_display_(gfx::GetXDisplay()), 325 x_root_window_(DefaultRootWindow(x_display_)), 326 x_window_(XCreateWindow( 327 x_display_, x_root_window_, 328 -100, -100, 10, 10, // x, y, width, height 329 0, // border width 330 CopyFromParent, // depth 331 InputOnly, 332 CopyFromParent, // visual 333 0, 334 NULL)), 335 atom_cache_(x_display_, kAtomsToCache), 336 clipboard_requestor_(x_display_, x_window_, 337 atom_cache_.GetAtom(kClipboard), this), 338 primary_requestor_(x_display_, x_window_, XA_PRIMARY, this), 339 clipboard_manager_requestor_(x_display_, x_window_, 340 atom_cache_.GetAtom(kClipboardManager), 341 this), 342 clipboard_owner_(x_display_, x_window_, atom_cache_.GetAtom(kClipboard)), 343 primary_owner_(x_display_, x_window_, XA_PRIMARY) { 344 // We don't know all possible MIME types at compile time. 345 atom_cache_.allow_uncached_atoms(); 346 347 XStoreName(x_display_, x_window_, "Chromium clipboard"); 348 XSelectInput(x_display_, x_window_, PropertyChangeMask); 349 350 if (PlatformEventSource::GetInstance()) 351 PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); 352 } 353 354 Clipboard::AuraX11Details::~AuraX11Details() { 355 if (PlatformEventSource::GetInstance()) 356 PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); 357 358 XDestroyWindow(x_display_, x_window_); 359 } 360 361 ::Atom Clipboard::AuraX11Details::LookupSelectionForClipboardType( 362 ClipboardType type) const { 363 if (type == CLIPBOARD_TYPE_COPY_PASTE) 364 return GetCopyPasteSelection(); 365 366 return XA_PRIMARY; 367 } 368 369 ::Atom Clipboard::AuraX11Details::GetCopyPasteSelection() const { 370 return atom_cache_.GetAtom(kClipboard); 371 } 372 373 const SelectionFormatMap& Clipboard::AuraX11Details::LookupStorageForAtom( 374 ::Atom atom) { 375 if (atom == XA_PRIMARY) 376 return primary_owner_.selection_format_map(); 377 378 DCHECK_EQ(GetCopyPasteSelection(), atom); 379 return clipboard_owner_.selection_format_map(); 380 } 381 382 ui::SelectionRequestor* 383 Clipboard::AuraX11Details::GetSelectionRequestorForClipboardType( 384 ClipboardType type) { 385 if (type == CLIPBOARD_TYPE_COPY_PASTE) 386 return &clipboard_requestor_; 387 else 388 return &primary_requestor_; 389 } 390 391 void Clipboard::AuraX11Details::CreateNewClipboardData() { 392 clipboard_data_ = SelectionFormatMap(); 393 } 394 395 void Clipboard::AuraX11Details::InsertMapping( 396 const std::string& key, 397 const scoped_refptr<base::RefCountedMemory>& memory) { 398 ::Atom atom_key = atom_cache_.GetAtom(key.c_str()); 399 clipboard_data_.Insert(atom_key, memory); 400 } 401 402 void Clipboard::AuraX11Details::TakeOwnershipOfSelection(ClipboardType type) { 403 if (type == CLIPBOARD_TYPE_COPY_PASTE) 404 return clipboard_owner_.TakeOwnershipOfSelection(clipboard_data_); 405 else 406 return primary_owner_.TakeOwnershipOfSelection(clipboard_data_); 407 } 408 409 SelectionData Clipboard::AuraX11Details::RequestAndWaitForTypes( 410 ClipboardType type, 411 const std::vector< ::Atom>& types) { 412 ::Atom selection_name = LookupSelectionForClipboardType(type); 413 if (XGetSelectionOwner(x_display_, selection_name) == x_window_) { 414 // We can local fastpath instead of playing the nested message loop game 415 // with the X server. 416 const SelectionFormatMap& format_map = LookupStorageForAtom(selection_name); 417 418 for (std::vector< ::Atom>::const_iterator it = types.begin(); 419 it != types.end(); ++it) { 420 SelectionFormatMap::const_iterator format_map_it = format_map.find(*it); 421 if (format_map_it != format_map.end()) 422 return SelectionData(format_map_it->first, format_map_it->second); 423 } 424 } else { 425 TargetList targets = WaitAndGetTargetsList(type); 426 SelectionRequestor* receiver = GetSelectionRequestorForClipboardType(type); 427 428 std::vector< ::Atom> intersection; 429 ui::GetAtomIntersection(types, targets.target_list(), &intersection); 430 return receiver->RequestAndWaitForTypes(intersection); 431 } 432 433 return SelectionData(); 434 } 435 436 TargetList Clipboard::AuraX11Details::WaitAndGetTargetsList( 437 ClipboardType type) { 438 ::Atom selection_name = LookupSelectionForClipboardType(type); 439 std::vector< ::Atom> out; 440 if (XGetSelectionOwner(x_display_, selection_name) == x_window_) { 441 // We can local fastpath and return the list of local targets. 442 const SelectionFormatMap& format_map = LookupStorageForAtom(selection_name); 443 444 for (SelectionFormatMap::const_iterator it = format_map.begin(); 445 it != format_map.end(); ++it) { 446 out.push_back(it->first); 447 } 448 } else { 449 scoped_refptr<base::RefCountedMemory> data; 450 size_t out_data_items = 0; 451 ::Atom out_type = None; 452 453 SelectionRequestor* receiver = GetSelectionRequestorForClipboardType(type); 454 if (receiver->PerformBlockingConvertSelection(atom_cache_.GetAtom(kTargets), 455 &data, 456 NULL, 457 &out_data_items, 458 &out_type)) { 459 // Some apps return an |out_type| of "TARGETS". (crbug.com/377893) 460 if (out_type == XA_ATOM || out_type == atom_cache_.GetAtom(kTargets)) { 461 const ::Atom* atom_array = 462 reinterpret_cast<const ::Atom*>(data->front()); 463 for (size_t i = 0; i < out_data_items; ++i) 464 out.push_back(atom_array[i]); 465 } 466 } else { 467 // There was no target list. Most Java apps doesn't offer a TARGETS list, 468 // even though they AWT to. They will offer individual text types if you 469 // ask. If this is the case we attempt to make sense of the contents as 470 // text. This is pretty unfortunate since it means we have to actually 471 // copy the data to see if it is available, but at least this path 472 // shouldn't be hit for conforming programs. 473 std::vector< ::Atom> types = GetTextAtoms(); 474 for (std::vector< ::Atom>::const_iterator it = types.begin(); 475 it != types.end(); ++it) { 476 ::Atom type = None; 477 if (receiver->PerformBlockingConvertSelection(*it, 478 NULL, 479 NULL, 480 NULL, 481 &type) && 482 type == *it) { 483 out.push_back(*it); 484 } 485 } 486 } 487 } 488 489 return TargetList(out, &atom_cache_); 490 } 491 492 std::vector< ::Atom> Clipboard::AuraX11Details::GetTextAtoms() const { 493 return GetTextAtomsFrom(&atom_cache_); 494 } 495 496 std::vector< ::Atom> Clipboard::AuraX11Details::GetAtomsForFormat( 497 const Clipboard::FormatType& format) { 498 std::vector< ::Atom> atoms; 499 atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str())); 500 return atoms; 501 } 502 503 void Clipboard::AuraX11Details::Clear(ClipboardType type) { 504 if (type == CLIPBOARD_TYPE_COPY_PASTE) 505 clipboard_owner_.ClearSelectionOwner(); 506 else 507 primary_owner_.ClearSelectionOwner(); 508 } 509 510 void Clipboard::AuraX11Details::StoreCopyPasteDataAndWait() { 511 ::Atom selection = GetCopyPasteSelection(); 512 if (XGetSelectionOwner(x_display_, selection) != x_window_) 513 return; 514 515 ::Atom clipboard_manager_atom = atom_cache_.GetAtom(kClipboardManager); 516 if (XGetSelectionOwner(x_display_, clipboard_manager_atom) == None) 517 return; 518 519 const SelectionFormatMap& format_map = LookupStorageForAtom(selection); 520 if (format_map.size() == 0) 521 return; 522 std::vector<Atom> targets = format_map.GetTypes(); 523 524 base::TimeTicks start = base::TimeTicks::Now(); 525 clipboard_manager_requestor_.PerformBlockingConvertSelectionWithParameter( 526 atom_cache_.GetAtom(kSaveTargets), targets); 527 UMA_HISTOGRAM_TIMES("Clipboard.X11StoreCopyPasteDuration", 528 base::TimeTicks::Now() - start); 529 } 530 531 bool Clipboard::AuraX11Details::CanDispatchEvent(const PlatformEvent& event) { 532 return event->xany.window == x_window_; 533 } 534 535 uint32_t Clipboard::AuraX11Details::DispatchEvent(const PlatformEvent& xev) { 536 switch (xev->type) { 537 case SelectionRequest: { 538 if (xev->xselectionrequest.selection == XA_PRIMARY) { 539 primary_owner_.OnSelectionRequest(xev->xselectionrequest); 540 } else { 541 // We should not get requests for the CLIPBOARD_MANAGER selection 542 // because we never take ownership of it. 543 DCHECK_EQ(GetCopyPasteSelection(), xev->xselectionrequest.selection); 544 clipboard_owner_.OnSelectionRequest(xev->xselectionrequest); 545 } 546 break; 547 } 548 case SelectionNotify: { 549 ::Atom selection = xev->xselection.selection; 550 if (selection == XA_PRIMARY) 551 primary_requestor_.OnSelectionNotify(xev->xselection); 552 else if (selection == GetCopyPasteSelection()) 553 clipboard_requestor_.OnSelectionNotify(xev->xselection); 554 else if (selection == atom_cache_.GetAtom(kClipboardManager)) 555 clipboard_manager_requestor_.OnSelectionNotify(xev->xselection); 556 break; 557 } 558 case SelectionClear: { 559 if (xev->xselectionclear.selection == XA_PRIMARY) { 560 primary_owner_.OnSelectionClear(xev->xselectionclear); 561 } else { 562 // We should not get requests for the CLIPBOARD_MANAGER selection 563 // because we never take ownership of it. 564 DCHECK_EQ(GetCopyPasteSelection(), xev->xselection.selection); 565 clipboard_owner_.OnSelectionClear(xev->xselectionclear); 566 } 567 break; 568 } 569 default: 570 break; 571 } 572 573 return POST_DISPATCH_NONE; 574 } 575 576 /////////////////////////////////////////////////////////////////////////////// 577 // Clipboard 578 579 Clipboard::Clipboard() 580 : aurax11_details_(new AuraX11Details) { 581 DCHECK(CalledOnValidThread()); 582 } 583 584 Clipboard::~Clipboard() { 585 DCHECK(CalledOnValidThread()); 586 587 aurax11_details_->StoreCopyPasteDataAndWait(); 588 } 589 590 void Clipboard::WriteObjects(ClipboardType type, const ObjectMap& objects) { 591 DCHECK(CalledOnValidThread()); 592 DCHECK(IsSupportedClipboardType(type)); 593 594 aurax11_details_->CreateNewClipboardData(); 595 for (ObjectMap::const_iterator iter = objects.begin(); 596 iter != objects.end(); ++iter) { 597 DispatchObject(static_cast<ObjectType>(iter->first), iter->second); 598 } 599 aurax11_details_->TakeOwnershipOfSelection(type); 600 601 if (type == CLIPBOARD_TYPE_COPY_PASTE) { 602 ObjectMap::const_iterator text_iter = objects.find(CBF_TEXT); 603 if (text_iter != objects.end()) { 604 aurax11_details_->CreateNewClipboardData(); 605 const ObjectMapParams& params_vector = text_iter->second; 606 if (params_vector.size()) { 607 const ObjectMapParam& char_vector = params_vector[0]; 608 if (char_vector.size()) 609 WriteText(&char_vector.front(), char_vector.size()); 610 } 611 aurax11_details_->TakeOwnershipOfSelection(CLIPBOARD_TYPE_SELECTION); 612 } 613 } 614 } 615 616 bool Clipboard::IsFormatAvailable(const FormatType& format, 617 ClipboardType type) const { 618 DCHECK(CalledOnValidThread()); 619 DCHECK(IsSupportedClipboardType(type)); 620 621 TargetList target_list = aurax11_details_->WaitAndGetTargetsList(type); 622 if (format.Equals(GetPlainTextFormatType()) || 623 format.Equals(GetUrlFormatType())) { 624 return target_list.ContainsText(); 625 } 626 return target_list.ContainsFormat(format); 627 } 628 629 void Clipboard::Clear(ClipboardType type) { 630 DCHECK(CalledOnValidThread()); 631 DCHECK(IsSupportedClipboardType(type)); 632 aurax11_details_->Clear(type); 633 } 634 635 void Clipboard::ReadAvailableTypes(ClipboardType type, 636 std::vector<base::string16>* types, 637 bool* contains_filenames) const { 638 DCHECK(CalledOnValidThread()); 639 if (!types || !contains_filenames) { 640 NOTREACHED(); 641 return; 642 } 643 644 TargetList target_list = aurax11_details_->WaitAndGetTargetsList(type); 645 646 types->clear(); 647 648 if (target_list.ContainsText()) 649 types->push_back(base::UTF8ToUTF16(kMimeTypeText)); 650 if (target_list.ContainsFormat(GetHtmlFormatType())) 651 types->push_back(base::UTF8ToUTF16(kMimeTypeHTML)); 652 if (target_list.ContainsFormat(GetRtfFormatType())) 653 types->push_back(base::UTF8ToUTF16(kMimeTypeRTF)); 654 if (target_list.ContainsFormat(GetBitmapFormatType())) 655 types->push_back(base::UTF8ToUTF16(kMimeTypePNG)); 656 *contains_filenames = false; 657 658 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 659 type, aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType()))); 660 if (data.IsValid()) 661 ReadCustomDataTypes(data.GetData(), data.GetSize(), types); 662 } 663 664 void Clipboard::ReadText(ClipboardType type, base::string16* result) const { 665 DCHECK(CalledOnValidThread()); 666 667 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 668 type, aurax11_details_->GetTextAtoms())); 669 if (data.IsValid()) { 670 std::string text = data.GetText(); 671 *result = base::UTF8ToUTF16(text); 672 } 673 } 674 675 void Clipboard::ReadAsciiText(ClipboardType type, std::string* result) const { 676 DCHECK(CalledOnValidThread()); 677 678 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 679 type, aurax11_details_->GetTextAtoms())); 680 if (data.IsValid()) 681 result->assign(data.GetText()); 682 } 683 684 // TODO(estade): handle different charsets. 685 // TODO(port): set *src_url. 686 void Clipboard::ReadHTML(ClipboardType type, 687 base::string16* markup, 688 std::string* src_url, 689 uint32* fragment_start, 690 uint32* fragment_end) const { 691 DCHECK(CalledOnValidThread()); 692 markup->clear(); 693 if (src_url) 694 src_url->clear(); 695 *fragment_start = 0; 696 *fragment_end = 0; 697 698 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 699 type, aurax11_details_->GetAtomsForFormat(GetHtmlFormatType()))); 700 if (data.IsValid()) { 701 *markup = data.GetHtml(); 702 703 *fragment_start = 0; 704 DCHECK(markup->length() <= kuint32max); 705 *fragment_end = static_cast<uint32>(markup->length()); 706 } 707 } 708 709 void Clipboard::ReadRTF(ClipboardType type, std::string* result) const { 710 DCHECK(CalledOnValidThread()); 711 712 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 713 type, aurax11_details_->GetAtomsForFormat(GetRtfFormatType()))); 714 if (data.IsValid()) 715 data.AssignTo(result); 716 } 717 718 SkBitmap Clipboard::ReadImage(ClipboardType type) const { 719 DCHECK(CalledOnValidThread()); 720 721 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 722 type, aurax11_details_->GetAtomsForFormat(GetBitmapFormatType()))); 723 if (data.IsValid()) { 724 SkBitmap bitmap; 725 if (gfx::PNGCodec::Decode(data.GetData(), data.GetSize(), &bitmap)) 726 return SkBitmap(bitmap); 727 } 728 729 return SkBitmap(); 730 } 731 732 void Clipboard::ReadCustomData(ClipboardType clipboard_type, 733 const base::string16& type, 734 base::string16* result) const { 735 DCHECK(CalledOnValidThread()); 736 737 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 738 clipboard_type, 739 aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType()))); 740 if (data.IsValid()) 741 ReadCustomDataForType(data.GetData(), data.GetSize(), type, result); 742 } 743 744 void Clipboard::ReadBookmark(base::string16* title, std::string* url) const { 745 DCHECK(CalledOnValidThread()); 746 // TODO(erg): This was left NOTIMPLEMENTED() in the gtk port too. 747 NOTIMPLEMENTED(); 748 } 749 750 void Clipboard::ReadData(const FormatType& format, std::string* result) const { 751 DCHECK(CalledOnValidThread()); 752 753 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 754 CLIPBOARD_TYPE_COPY_PASTE, aurax11_details_->GetAtomsForFormat(format))); 755 if (data.IsValid()) 756 data.AssignTo(result); 757 } 758 759 uint64 Clipboard::GetSequenceNumber(ClipboardType type) { 760 DCHECK(CalledOnValidThread()); 761 if (type == CLIPBOARD_TYPE_COPY_PASTE) 762 return SelectionChangeObserver::GetInstance()->clipboard_sequence_number(); 763 else 764 return SelectionChangeObserver::GetInstance()->primary_sequence_number(); 765 } 766 767 void Clipboard::WriteText(const char* text_data, size_t text_len) { 768 std::string str(text_data, text_len); 769 scoped_refptr<base::RefCountedMemory> mem( 770 base::RefCountedString::TakeString(&str)); 771 772 aurax11_details_->InsertMapping(kMimeTypeText, mem); 773 aurax11_details_->InsertMapping(kText, mem); 774 aurax11_details_->InsertMapping(kString, mem); 775 aurax11_details_->InsertMapping(kUtf8String, mem); 776 } 777 778 void Clipboard::WriteHTML(const char* markup_data, 779 size_t markup_len, 780 const char* url_data, 781 size_t url_len) { 782 // TODO(estade): We need to expand relative links with |url_data|. 783 static const char* html_prefix = "<meta http-equiv=\"content-type\" " 784 "content=\"text/html; charset=utf-8\">"; 785 std::string data = html_prefix; 786 data += std::string(markup_data, markup_len); 787 // Some programs expect NULL-terminated data. See http://crbug.com/42624 788 data += '\0'; 789 790 scoped_refptr<base::RefCountedMemory> mem( 791 base::RefCountedString::TakeString(&data)); 792 aurax11_details_->InsertMapping(kMimeTypeHTML, mem); 793 } 794 795 void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) { 796 WriteData(GetRtfFormatType(), rtf_data, data_len); 797 } 798 799 void Clipboard::WriteBookmark(const char* title_data, 800 size_t title_len, 801 const char* url_data, 802 size_t url_len) { 803 // Write as a mozilla url (UTF16: URL, newline, title). 804 base::string16 url = base::UTF8ToUTF16(std::string(url_data, url_len) + "\n"); 805 base::string16 title = base::UTF8ToUTF16(std::string(title_data, title_len)); 806 807 std::vector<unsigned char> data; 808 ui::AddString16ToVector(url, &data); 809 ui::AddString16ToVector(title, &data); 810 scoped_refptr<base::RefCountedMemory> mem( 811 base::RefCountedBytes::TakeVector(&data)); 812 813 aurax11_details_->InsertMapping(kMimeTypeMozillaURL, mem); 814 } 815 816 // Write an extra flavor that signifies WebKit was the last to modify the 817 // pasteboard. This flavor has no data. 818 void Clipboard::WriteWebSmartPaste() { 819 std::string empty; 820 aurax11_details_->InsertMapping( 821 kMimeTypeWebkitSmartPaste, 822 scoped_refptr<base::RefCountedMemory>( 823 base::RefCountedString::TakeString(&empty))); 824 } 825 826 void Clipboard::WriteBitmap(const SkBitmap& bitmap) { 827 // Encode the bitmap as a PNG for transport. 828 std::vector<unsigned char> output; 829 if (gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, false, &output)) { 830 aurax11_details_->InsertMapping(kMimeTypePNG, 831 base::RefCountedBytes::TakeVector( 832 &output)); 833 } 834 } 835 836 void Clipboard::WriteData(const FormatType& format, 837 const char* data_data, 838 size_t data_len) { 839 // We assume that certain mapping types are only written by trusted code. 840 // Therefore we must upkeep their integrity. 841 if (format.Equals(GetBitmapFormatType())) 842 return; 843 844 std::vector<unsigned char> bytes(data_data, data_data + data_len); 845 scoped_refptr<base::RefCountedMemory> mem( 846 base::RefCountedBytes::TakeVector(&bytes)); 847 aurax11_details_->InsertMapping(format.ToString(), mem); 848 } 849 850 // static 851 Clipboard::FormatType Clipboard::GetFormatType( 852 const std::string& format_string) { 853 return FormatType::Deserialize(format_string); 854 } 855 856 // static 857 const Clipboard::FormatType& Clipboard::GetUrlFormatType() { 858 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeURIList)); 859 return type; 860 } 861 862 // static 863 const Clipboard::FormatType& Clipboard::GetUrlWFormatType() { 864 return GetUrlFormatType(); 865 } 866 867 // static 868 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { 869 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeText)); 870 return type; 871 } 872 873 // static 874 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { 875 return GetPlainTextFormatType(); 876 } 877 878 // static 879 const Clipboard::FormatType& Clipboard::GetFilenameFormatType() { 880 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeFilename)); 881 return type; 882 } 883 884 // static 885 const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() { 886 return Clipboard::GetFilenameFormatType(); 887 } 888 889 // static 890 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { 891 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeHTML)); 892 return type; 893 } 894 895 // static 896 const Clipboard::FormatType& Clipboard::GetRtfFormatType() { 897 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeRTF)); 898 return type; 899 } 900 901 // static 902 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { 903 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePNG)); 904 return type; 905 } 906 907 // static 908 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { 909 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebkitSmartPaste)); 910 return type; 911 } 912 913 // static 914 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { 915 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); 916 return type; 917 } 918 919 // static 920 const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() { 921 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData)); 922 return type; 923 } 924 925 } // namespace ui 926