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/message_loop/message_pump_observer.h" 19 #include "base/message_loop/message_pump_x11.h" 20 #include "base/stl_util.h" 21 #include "base/strings/utf_string_conversions.h" 22 #include "third_party/skia/include/core/SkBitmap.h" 23 #include "ui/base/clipboard/custom_data_helper.h" 24 #include "ui/base/x/selection_owner.h" 25 #include "ui/base/x/selection_requestor.h" 26 #include "ui/base/x/selection_utils.h" 27 #include "ui/base/x/x11_util.h" 28 #include "ui/gfx/codec/png_codec.h" 29 #include "ui/gfx/size.h" 30 #include "ui/gfx/x/x11_atom_cache.h" 31 32 namespace ui { 33 34 namespace { 35 36 const char kClipboard[] = "CLIPBOARD"; 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 Clipboard::kMimeTypePNG, 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(gfx::GetXDisplay(), &event_base_, &ignored)) { 94 clipboard_atom_ = XInternAtom(gfx::GetXDisplay(), kClipboard, false); 95 XFixesSelectSelectionInput(gfx::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(gfx::GetXDisplay(), GetX11RootWindow(), 104 XA_PRIMARY, 105 XFixesSetSelectionOwnerNotifyMask | 106 XFixesSelectionWindowDestroyNotifyMask | 107 XFixesSelectionClientCloseNotifyMask); 108 109 base::MessagePumpX11::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 type. 236 ::Atom LookupSelectionForClipboardType(ClipboardType type) const; 237 238 // Returns the object which is responsible for communication on |type|. 239 SelectionRequestor* GetSelectionRequestorForClipboardType(ClipboardType type); 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 // |type|. 256 void TakeOwnershipOfSelection(ClipboardType type); 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(ClipboardType type, 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(ClipboardType type); 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 clipboard type. 282 void Clear(ClipboardType type); 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_(gfx::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::MessagePumpX11::Current()->AddDispatcherForWindow(this, x_window_); 336 } 337 338 Clipboard::AuraX11Details::~AuraX11Details() { 339 base::MessagePumpX11::Current()->RemoveDispatcherForWindow(x_window_); 340 341 XDestroyWindow(x_display_, x_window_); 342 } 343 344 ::Atom Clipboard::AuraX11Details::LookupSelectionForClipboardType( 345 ClipboardType type) const { 346 if (type == CLIPBOARD_TYPE_COPY_PASTE) 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::GetSelectionRequestorForClipboardType( 363 ClipboardType type) { 364 if (type == CLIPBOARD_TYPE_COPY_PASTE) 365 return &clipboard_requestor_; 366 else 367 return &primary_requestor_; 368 } 369 370 void Clipboard::AuraX11Details::CreateNewClipboardData() { 371 clipboard_data_ = SelectionFormatMap(); 372 } 373 374 void Clipboard::AuraX11Details::InsertMapping( 375 const std::string& key, 376 const scoped_refptr<base::RefCountedMemory>& memory) { 377 ::Atom atom_key = atom_cache_.GetAtom(key.c_str()); 378 clipboard_data_.Insert(atom_key, memory); 379 } 380 381 void Clipboard::AuraX11Details::TakeOwnershipOfSelection(ClipboardType type) { 382 if (type == CLIPBOARD_TYPE_COPY_PASTE) 383 return clipboard_owner_.TakeOwnershipOfSelection(clipboard_data_); 384 else 385 return primary_owner_.TakeOwnershipOfSelection(clipboard_data_); 386 } 387 388 SelectionData Clipboard::AuraX11Details::RequestAndWaitForTypes( 389 ClipboardType type, 390 const std::vector< ::Atom>& types) { 391 ::Atom selection_name = LookupSelectionForClipboardType(type); 392 if (XGetSelectionOwner(x_display_, selection_name) == x_window_) { 393 // We can local fastpath instead of playing the nested message loop game 394 // with the X server. 395 const SelectionFormatMap& format_map = LookupStorageForAtom(selection_name); 396 397 for (std::vector< ::Atom>::const_iterator it = types.begin(); 398 it != types.end(); ++it) { 399 SelectionFormatMap::const_iterator format_map_it = format_map.find(*it); 400 if (format_map_it != format_map.end()) 401 return SelectionData(format_map_it->first, format_map_it->second); 402 } 403 } else { 404 TargetList targets = WaitAndGetTargetsList(type); 405 SelectionRequestor* receiver = GetSelectionRequestorForClipboardType(type); 406 407 std::vector< ::Atom> intersection; 408 ui::GetAtomIntersection(types, targets.target_list(), &intersection); 409 return receiver->RequestAndWaitForTypes(intersection); 410 } 411 412 return SelectionData(); 413 } 414 415 TargetList Clipboard::AuraX11Details::WaitAndGetTargetsList( 416 ClipboardType type) { 417 ::Atom selection_name = LookupSelectionForClipboardType(type); 418 std::vector< ::Atom> out; 419 if (XGetSelectionOwner(x_display_, selection_name) == x_window_) { 420 // We can local fastpath and return the list of local targets. 421 const SelectionFormatMap& format_map = LookupStorageForAtom(selection_name); 422 423 for (SelectionFormatMap::const_iterator it = format_map.begin(); 424 it != format_map.end(); ++it) { 425 out.push_back(it->first); 426 } 427 } else { 428 scoped_refptr<base::RefCountedMemory> data; 429 size_t out_data_items = 0; 430 ::Atom out_type = None; 431 432 SelectionRequestor* receiver = GetSelectionRequestorForClipboardType(type); 433 if (receiver->PerformBlockingConvertSelection(atom_cache_.GetAtom(kTargets), 434 &data, 435 NULL, 436 &out_data_items, 437 &out_type)) { 438 const ::Atom* atom_array = reinterpret_cast<const ::Atom*>(data->front()); 439 for (size_t i = 0; i < out_data_items; ++i) 440 out.push_back(atom_array[i]); 441 } else { 442 // There was no target list. Most Java apps doesn't offer a TARGETS list, 443 // even though they AWT to. They will offer individual text types if you 444 // ask. If this is the case we attempt to make sense of the contents as 445 // text. This is pretty unfortunate since it means we have to actually 446 // copy the data to see if it is available, but at least this path 447 // shouldn't be hit for conforming programs. 448 std::vector< ::Atom> types = GetTextAtoms(); 449 for (std::vector< ::Atom>::const_iterator it = types.begin(); 450 it != types.end(); ++it) { 451 ::Atom type = None; 452 if (receiver->PerformBlockingConvertSelection(*it, 453 NULL, 454 NULL, 455 NULL, 456 &type) && 457 type == *it) { 458 out.push_back(*it); 459 } 460 } 461 } 462 } 463 464 return TargetList(out, &atom_cache_); 465 } 466 467 std::vector< ::Atom> Clipboard::AuraX11Details::GetTextAtoms() const { 468 return GetTextAtomsFrom(&atom_cache_); 469 } 470 471 std::vector< ::Atom> Clipboard::AuraX11Details::GetAtomsForFormat( 472 const Clipboard::FormatType& format) { 473 std::vector< ::Atom> atoms; 474 atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str())); 475 return atoms; 476 } 477 478 void Clipboard::AuraX11Details::Clear(ClipboardType type) { 479 if (type == CLIPBOARD_TYPE_COPY_PASTE) 480 return clipboard_owner_.Clear(); 481 else 482 return primary_owner_.Clear(); 483 } 484 485 bool Clipboard::AuraX11Details::Dispatch(const base::NativeEvent& event) { 486 XEvent* xev = event; 487 488 switch (xev->type) { 489 case SelectionRequest: { 490 if (xev->xselectionrequest.selection == XA_PRIMARY) 491 primary_owner_.OnSelectionRequest(xev->xselectionrequest); 492 else 493 clipboard_owner_.OnSelectionRequest(xev->xselectionrequest); 494 break; 495 } 496 case SelectionNotify: { 497 if (xev->xselection.selection == XA_PRIMARY) 498 primary_requestor_.OnSelectionNotify(xev->xselection); 499 else 500 clipboard_requestor_.OnSelectionNotify(xev->xselection); 501 break; 502 } 503 case SelectionClear: { 504 if (xev->xselectionclear.selection == XA_PRIMARY) 505 primary_owner_.OnSelectionClear(xev->xselectionclear); 506 else 507 clipboard_owner_.OnSelectionClear(xev->xselectionclear); 508 break; 509 } 510 default: 511 break; 512 } 513 514 return true; 515 } 516 517 /////////////////////////////////////////////////////////////////////////////// 518 // Clipboard 519 520 Clipboard::Clipboard() 521 : aurax11_details_(new AuraX11Details) { 522 DCHECK(CalledOnValidThread()); 523 } 524 525 Clipboard::~Clipboard() { 526 DCHECK(CalledOnValidThread()); 527 528 // TODO(erg): We need to do whatever the equivalent of 529 // gtk_clipboard_store(clipboard_) is here. When we shut down, we want the 530 // current selection to live on. 531 } 532 533 void Clipboard::WriteObjects(ClipboardType type, const ObjectMap& objects) { 534 DCHECK(CalledOnValidThread()); 535 DCHECK(IsSupportedClipboardType(type)); 536 537 aurax11_details_->CreateNewClipboardData(); 538 for (ObjectMap::const_iterator iter = objects.begin(); 539 iter != objects.end(); ++iter) { 540 DispatchObject(static_cast<ObjectType>(iter->first), iter->second); 541 } 542 aurax11_details_->TakeOwnershipOfSelection(type); 543 544 if (type == CLIPBOARD_TYPE_COPY_PASTE) { 545 ObjectMap::const_iterator text_iter = objects.find(CBF_TEXT); 546 if (text_iter != objects.end()) { 547 aurax11_details_->CreateNewClipboardData(); 548 const ObjectMapParam& char_vector = text_iter->second[0]; 549 WriteText(&char_vector.front(), char_vector.size()); 550 aurax11_details_->TakeOwnershipOfSelection(CLIPBOARD_TYPE_SELECTION); 551 } 552 } 553 } 554 555 bool Clipboard::IsFormatAvailable(const FormatType& format, 556 ClipboardType type) const { 557 DCHECK(CalledOnValidThread()); 558 DCHECK(IsSupportedClipboardType(type)); 559 560 TargetList target_list = aurax11_details_->WaitAndGetTargetsList(type); 561 return target_list.ContainsFormat(format); 562 } 563 564 void Clipboard::Clear(ClipboardType type) { 565 DCHECK(CalledOnValidThread()); 566 DCHECK(IsSupportedClipboardType(type)); 567 aurax11_details_->Clear(type); 568 } 569 570 void Clipboard::ReadAvailableTypes(ClipboardType type, 571 std::vector<string16>* types, 572 bool* contains_filenames) const { 573 DCHECK(CalledOnValidThread()); 574 if (!types || !contains_filenames) { 575 NOTREACHED(); 576 return; 577 } 578 579 TargetList target_list = aurax11_details_->WaitAndGetTargetsList(type); 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 type, aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType()))); 595 if (data.IsValid()) 596 ReadCustomDataTypes(data.GetData(), data.GetSize(), types); 597 } 598 599 void Clipboard::ReadText(ClipboardType type, string16* result) const { 600 DCHECK(CalledOnValidThread()); 601 602 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 603 type, aurax11_details_->GetTextAtoms())); 604 if (data.IsValid()) { 605 std::string text = data.GetText(); 606 *result = UTF8ToUTF16(text); 607 } 608 } 609 610 void Clipboard::ReadAsciiText(ClipboardType type, std::string* result) const { 611 DCHECK(CalledOnValidThread()); 612 613 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 614 type, aurax11_details_->GetTextAtoms())); 615 if (data.IsValid()) 616 result->assign(data.GetText()); 617 } 618 619 // TODO(estade): handle different charsets. 620 // TODO(port): set *src_url. 621 void Clipboard::ReadHTML(ClipboardType type, 622 string16* markup, 623 std::string* src_url, 624 uint32* fragment_start, 625 uint32* fragment_end) const { 626 DCHECK(CalledOnValidThread()); 627 markup->clear(); 628 if (src_url) 629 src_url->clear(); 630 *fragment_start = 0; 631 *fragment_end = 0; 632 633 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 634 type, aurax11_details_->GetAtomsForFormat(GetHtmlFormatType()))); 635 if (data.IsValid()) { 636 *markup = data.GetHtml(); 637 638 *fragment_start = 0; 639 DCHECK(markup->length() <= kuint32max); 640 *fragment_end = static_cast<uint32>(markup->length()); 641 } 642 } 643 644 void Clipboard::ReadRTF(ClipboardType type, std::string* result) const { 645 DCHECK(CalledOnValidThread()); 646 647 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 648 type, aurax11_details_->GetAtomsForFormat(GetRtfFormatType()))); 649 if (data.IsValid()) 650 data.AssignTo(result); 651 } 652 653 SkBitmap Clipboard::ReadImage(ClipboardType type) const { 654 DCHECK(CalledOnValidThread()); 655 656 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 657 type, aurax11_details_->GetAtomsForFormat(GetBitmapFormatType()))); 658 if (data.IsValid()) { 659 SkBitmap bitmap; 660 if (gfx::PNGCodec::Decode(data.GetData(), data.GetSize(), &bitmap)) 661 return SkBitmap(bitmap); 662 } 663 664 return SkBitmap(); 665 } 666 667 void Clipboard::ReadCustomData(ClipboardType clipboard_type, 668 const string16& type, 669 string16* result) const { 670 DCHECK(CalledOnValidThread()); 671 672 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 673 clipboard_type, 674 aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType()))); 675 if (data.IsValid()) 676 ReadCustomDataForType(data.GetData(), data.GetSize(), type, result); 677 } 678 679 void Clipboard::ReadBookmark(string16* title, std::string* url) const { 680 DCHECK(CalledOnValidThread()); 681 // TODO(erg): This was left NOTIMPLEMENTED() in the gtk port too. 682 NOTIMPLEMENTED(); 683 } 684 685 void Clipboard::ReadData(const FormatType& format, std::string* result) const { 686 DCHECK(CalledOnValidThread()); 687 688 SelectionData data(aurax11_details_->RequestAndWaitForTypes( 689 CLIPBOARD_TYPE_COPY_PASTE, aurax11_details_->GetAtomsForFormat(format))); 690 if (data.IsValid()) 691 data.AssignTo(result); 692 } 693 694 uint64 Clipboard::GetSequenceNumber(ClipboardType type) { 695 DCHECK(CalledOnValidThread()); 696 if (type == CLIPBOARD_TYPE_COPY_PASTE) 697 return SelectionChangeObserver::GetInstance()->clipboard_sequence_number(); 698 else 699 return SelectionChangeObserver::GetInstance()->primary_sequence_number(); 700 } 701 702 void Clipboard::WriteText(const char* text_data, size_t text_len) { 703 std::string str(text_data, text_len); 704 scoped_refptr<base::RefCountedMemory> mem( 705 base::RefCountedString::TakeString(&str)); 706 707 aurax11_details_->InsertMapping(kMimeTypeText, mem); 708 aurax11_details_->InsertMapping(kText, mem); 709 aurax11_details_->InsertMapping(kString, mem); 710 aurax11_details_->InsertMapping(kUtf8String, mem); 711 } 712 713 void Clipboard::WriteHTML(const char* markup_data, 714 size_t markup_len, 715 const char* url_data, 716 size_t url_len) { 717 // TODO(estade): We need to expand relative links with |url_data|. 718 static const char* html_prefix = "<meta http-equiv=\"content-type\" " 719 "content=\"text/html; charset=utf-8\">"; 720 std::string data = html_prefix; 721 data += std::string(markup_data, markup_len); 722 // Some programs expect NULL-terminated data. See http://crbug.com/42624 723 data += '\0'; 724 725 scoped_refptr<base::RefCountedMemory> mem( 726 base::RefCountedString::TakeString(&data)); 727 aurax11_details_->InsertMapping(kMimeTypeHTML, mem); 728 } 729 730 void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) { 731 WriteData(GetRtfFormatType(), rtf_data, data_len); 732 } 733 734 void Clipboard::WriteBookmark(const char* title_data, 735 size_t title_len, 736 const char* url_data, 737 size_t url_len) { 738 // Write as a mozilla url (UTF16: URL, newline, title). 739 string16 url = UTF8ToUTF16(std::string(url_data, url_len) + "\n"); 740 string16 title = UTF8ToUTF16(std::string(title_data, title_len)); 741 742 std::vector<unsigned char> data; 743 ui::AddString16ToVector(url, &data); 744 ui::AddString16ToVector(title, &data); 745 scoped_refptr<base::RefCountedMemory> mem( 746 base::RefCountedBytes::TakeVector(&data)); 747 748 aurax11_details_->InsertMapping(kMimeTypeMozillaURL, mem); 749 } 750 751 // Write an extra flavor that signifies WebKit was the last to modify the 752 // pasteboard. This flavor has no data. 753 void Clipboard::WriteWebSmartPaste() { 754 aurax11_details_->InsertMapping(kMimeTypeWebkitSmartPaste, 755 scoped_refptr<base::RefCountedMemory>()); 756 } 757 758 void Clipboard::WriteBitmap(const SkBitmap& bitmap) { 759 // Encode the bitmap as a PNG for transport. 760 std::vector<unsigned char> output; 761 if (gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, false, &output)) { 762 aurax11_details_->InsertMapping(kMimeTypePNG, 763 base::RefCountedBytes::TakeVector( 764 &output)); 765 } 766 } 767 768 void Clipboard::WriteData(const FormatType& format, 769 const char* data_data, 770 size_t data_len) { 771 // We assume that certain mapping types are only written by trusted code. 772 // Therefore we must upkeep their integrity. 773 if (format.Equals(GetBitmapFormatType())) 774 return; 775 776 std::vector<unsigned char> bytes(data_data, data_data + data_len); 777 scoped_refptr<base::RefCountedMemory> mem( 778 base::RefCountedBytes::TakeVector(&bytes)); 779 aurax11_details_->InsertMapping(format.ToString(), mem); 780 } 781 782 // static 783 Clipboard::FormatType Clipboard::GetFormatType( 784 const std::string& format_string) { 785 return FormatType::Deserialize(format_string); 786 } 787 788 // static 789 const Clipboard::FormatType& Clipboard::GetUrlFormatType() { 790 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeURIList)); 791 return type; 792 } 793 794 // static 795 const Clipboard::FormatType& Clipboard::GetUrlWFormatType() { 796 return GetUrlFormatType(); 797 } 798 799 // static 800 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { 801 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeText)); 802 return type; 803 } 804 805 // static 806 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { 807 return GetPlainTextFormatType(); 808 } 809 810 // static 811 const Clipboard::FormatType& Clipboard::GetFilenameFormatType() { 812 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeFilename)); 813 return type; 814 } 815 816 // static 817 const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() { 818 return Clipboard::GetFilenameFormatType(); 819 } 820 821 // static 822 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { 823 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeHTML)); 824 return type; 825 } 826 827 // static 828 const Clipboard::FormatType& Clipboard::GetRtfFormatType() { 829 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeRTF)); 830 return type; 831 } 832 833 // static 834 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { 835 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePNG)); 836 return type; 837 } 838 839 // static 840 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { 841 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebkitSmartPaste)); 842 return type; 843 } 844 845 // static 846 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { 847 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); 848 return type; 849 } 850 851 // static 852 const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() { 853 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData)); 854 return type; 855 } 856 857 } // namespace ui 858