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