root/ui/base/clipboard/clipboard_aurax11.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. clipboard_sequence_number
  2. primary_sequence_number
  3. DidProcessEvent
  4. primary_sequence_number_
  5. GetInstance
  6. WillProcessEvent
  7. target_list
  8. atom_cache_
  9. ContainsText
  10. ContainsFormat
  11. ContainsAtom
  12. Serialize
  13. Deserialize
  14. Equals
  15. atom_cache
  16. primary_owner_
  17. LookupSelectionForClipboardType
  18. LookupStorageForAtom
  19. GetSelectionRequestorForClipboardType
  20. CreateNewClipboardData
  21. InsertMapping
  22. TakeOwnershipOfSelection
  23. RequestAndWaitForTypes
  24. WaitAndGetTargetsList
  25. GetTextAtoms
  26. GetAtomsForFormat
  27. Clear
  28. Dispatch
  29. WriteObjects
  30. IsFormatAvailable
  31. Clear
  32. ReadAvailableTypes
  33. ReadText
  34. ReadAsciiText
  35. ReadHTML
  36. ReadRTF
  37. ReadImage
  38. ReadCustomData
  39. ReadBookmark
  40. ReadData
  41. GetSequenceNumber
  42. WriteText
  43. WriteHTML
  44. WriteRTF
  45. WriteBookmark
  46. WriteWebSmartPaste
  47. WriteBitmap
  48. WriteData
  49. GetFormatType
  50. GetUrlFormatType
  51. GetUrlWFormatType
  52. GetPlainTextFormatType
  53. GetPlainTextWFormatType
  54. GetFilenameFormatType
  55. GetFilenameWFormatType
  56. GetHtmlFormatType
  57. GetRtfFormatType
  58. GetBitmapFormatType
  59. GetWebKitSmartPasteFormatType
  60. GetWebCustomDataFormatType
  61. GetPepperCustomDataFormatType

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/base/clipboard/clipboard.h"

#include <X11/extensions/Xfixes.h>
#include <X11/Xatom.h>
#include <list>
#include <set>

#include "base/basictypes.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
#include "base/message_loop/message_pump_observer.h"
#include "base/message_loop/message_pump_x11.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/base/x/selection_owner.h"
#include "ui/base/x/selection_requestor.h"
#include "ui/base/x/selection_utils.h"
#include "ui/base/x/x11_util.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/size.h"
#include "ui/gfx/x/x11_atom_cache.h"

namespace ui {

namespace {

const char kClipboard[] = "CLIPBOARD";
const char kMimeTypeFilename[] = "chromium/filename";
const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data";
const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste";
const char kTargets[] = "TARGETS";

const char* kAtomsToCache[] = {
  kClipboard,
  Clipboard::kMimeTypePNG,
  kMimeTypeFilename,
  kMimeTypeMozillaURL,
  kMimeTypeWebkitSmartPaste,
  kString,
  kTargets,
  kText,
  kUtf8String,
  NULL
};

///////////////////////////////////////////////////////////////////////////////

// Uses the XFixes API to provide sequence numbers for GetSequenceNumber().
class SelectionChangeObserver : public base::MessagePumpObserver {
 public:
  static SelectionChangeObserver* GetInstance();

  uint64 clipboard_sequence_number() const {
    return clipboard_sequence_number_;
  }
  uint64 primary_sequence_number() const { return primary_sequence_number_; }

 private:
  friend struct DefaultSingletonTraits<SelectionChangeObserver>;

  SelectionChangeObserver();
  virtual ~SelectionChangeObserver();

  // Overridden from base::MessagePumpObserver:
  virtual base::EventStatus WillProcessEvent(
      const base::NativeEvent& event) OVERRIDE;
  virtual void DidProcessEvent(
      const base::NativeEvent& event) OVERRIDE {}

  int event_base_;
  Atom clipboard_atom_;
  uint64 clipboard_sequence_number_;
  uint64 primary_sequence_number_;

  DISALLOW_COPY_AND_ASSIGN(SelectionChangeObserver);
};

SelectionChangeObserver::SelectionChangeObserver()
    : event_base_(-1),
      clipboard_atom_(None),
      clipboard_sequence_number_(0),
      primary_sequence_number_(0) {
  int ignored;
  if (XFixesQueryExtension(gfx::GetXDisplay(), &event_base_, &ignored)) {
    clipboard_atom_ = XInternAtom(gfx::GetXDisplay(), kClipboard, false);
    XFixesSelectSelectionInput(gfx::GetXDisplay(), GetX11RootWindow(),
                               clipboard_atom_,
                               XFixesSetSelectionOwnerNotifyMask |
                               XFixesSelectionWindowDestroyNotifyMask |
                               XFixesSelectionClientCloseNotifyMask);
    // This seems to be semi-optional. For some reason, registering for any
    // selection notify events seems to subscribe us to events for both the
    // primary and the clipboard buffers. Register anyway just to be safe.
    XFixesSelectSelectionInput(gfx::GetXDisplay(), GetX11RootWindow(),
                               XA_PRIMARY,
                               XFixesSetSelectionOwnerNotifyMask |
                               XFixesSelectionWindowDestroyNotifyMask |
                               XFixesSelectionClientCloseNotifyMask);

    base::MessagePumpX11::Current()->AddObserver(this);
  }
}

SelectionChangeObserver::~SelectionChangeObserver() {
  // We are a singleton; we will outlive our message pump.
}

SelectionChangeObserver* SelectionChangeObserver::GetInstance() {
  return Singleton<SelectionChangeObserver>::get();
}

base::EventStatus SelectionChangeObserver::WillProcessEvent(
    const base::NativeEvent& event) {
  if (event->type == event_base_ + XFixesSelectionNotify) {
    XFixesSelectionNotifyEvent* ev =
        reinterpret_cast<XFixesSelectionNotifyEvent*>(event);
    if (ev->selection == clipboard_atom_) {
      clipboard_sequence_number_++;
    } else if (ev->selection == XA_PRIMARY) {
      primary_sequence_number_++;
    } else {
      DLOG(ERROR) << "Unexpected selection atom: " << ev->selection;
    }
  }
  return base::EVENT_CONTINUE;
}

///////////////////////////////////////////////////////////////////////////////

// Represents a list of possible return types. Copy constructable.
class TargetList {
 public:
  typedef std::vector< ::Atom> AtomVector;

  TargetList(const AtomVector& target_list, X11AtomCache* atom_cache);

  const AtomVector& target_list() { return target_list_; }

  bool ContainsText() const;
  bool ContainsFormat(const Clipboard::FormatType& format_type) const;
  bool ContainsAtom(::Atom atom) const;

 private:
  AtomVector target_list_;
  X11AtomCache* atom_cache_;
};

TargetList::TargetList(const AtomVector& target_list,
                       X11AtomCache* atom_cache)
    : target_list_(target_list),
      atom_cache_(atom_cache) {
}

bool TargetList::ContainsText() const {
  std::vector< ::Atom> atoms = GetTextAtomsFrom(atom_cache_);
  for (std::vector< ::Atom>::const_iterator it = atoms.begin();
       it != atoms.end(); ++it) {
    if (ContainsAtom(*it))
      return true;
  }

  return false;
}

bool TargetList::ContainsFormat(
    const Clipboard::FormatType& format_type) const {
  ::Atom atom = atom_cache_->GetAtom(format_type.ToString().c_str());
  return ContainsAtom(atom);
}

bool TargetList::ContainsAtom(::Atom atom) const {
  return find(target_list_.begin(), target_list_.end(), atom)
      != target_list_.end();
}

}  // namespace

///////////////////////////////////////////////////////////////////////////////

// I would love for the FormatType to really be a wrapper around an X11 ::Atom,
// but there are a few problems. Chromeos unit tests spawn a new X11 server for
// each test, so Atom numeric values don't persist across tests. We could still
// maybe deal with that if we didn't have static accessor methods everywhere.

Clipboard::FormatType::FormatType() {
}

Clipboard::FormatType::FormatType(const std::string& native_format)
    : data_(native_format) {
}

Clipboard::FormatType::~FormatType() {
}

std::string Clipboard::FormatType::Serialize() const {
  return data_;
}

// static
Clipboard::FormatType Clipboard::FormatType::Deserialize(
    const std::string& serialization) {
  return FormatType(serialization);
}

bool Clipboard::FormatType::operator<(const FormatType& other) const {
  return data_ < other.data_;
}

bool Clipboard::FormatType::Equals(const FormatType& other) const {
  return data_ == other.data_;
}

///////////////////////////////////////////////////////////////////////////////
// Clipboard::AuraX11Details

// Private implementation of our X11 integration. Keeps X11 headers out of the
// majority of chrome, which break badly.
class Clipboard::AuraX11Details : public base::MessagePumpDispatcher {
 public:
  AuraX11Details();
  virtual ~AuraX11Details();

  X11AtomCache* atom_cache() { return &atom_cache_; }

  // Returns the X11 type that we pass to various XSelection functions for the
  // given type.
  ::Atom LookupSelectionForClipboardType(ClipboardType type) const;

  // Returns the object which is responsible for communication on |type|.
  SelectionRequestor* GetSelectionRequestorForClipboardType(ClipboardType type);

  // Finds the SelectionFormatMap for the incoming selection atom.
  const SelectionFormatMap& LookupStorageForAtom(::Atom atom);

  // As we need to collect all the data types before we tell X11 that we own a
  // particular selection, we create a temporary clipboard mapping that
  // InsertMapping writes to. Then we commit it in TakeOwnershipOfSelection,
  // where we save it in one of the clipboard data slots.
  void CreateNewClipboardData();

  // Inserts a mapping into clipboard_data_.
  void InsertMapping(const std::string& key,
                     const scoped_refptr<base::RefCountedMemory>& memory);

  // Moves the temporary |clipboard_data_| to the long term data storage for
  // |type|.
  void TakeOwnershipOfSelection(ClipboardType type);

  // Returns the first of |types| offered by the current selection holder in
  // |data_out|, or returns NULL if none of those types are available.
  //
  // If the selection holder is us, this call is synchronous and we pull
  // the data out of |clipboard_selection_| or |primary_selection_|. If the
  // selection holder is some other window, we spin up a nested message loop
  // and do the asynchronous dance with whatever application is holding the
  // selection.
  ui::SelectionData RequestAndWaitForTypes(ClipboardType type,
                                           const std::vector< ::Atom>& types);

  // Retrieves the list of possible data types the current clipboard owner has.
  //
  // If the selection holder is us, this is synchronous, otherwise this runs a
  // blocking message loop.
  TargetList WaitAndGetTargetsList(ClipboardType type);

  // Returns a list of all text atoms that we handle.
  std::vector< ::Atom> GetTextAtoms() const;

  // Returns a vector with a |format| converted to an X11 atom.
  std::vector< ::Atom> GetAtomsForFormat(const Clipboard::FormatType& format);

  // Clears a certain clipboard type, whether we own it or not.
  void Clear(ClipboardType type);

 private:
  // Overridden from base::MessagePumpDispatcher:
  virtual uint32_t Dispatch(const base::NativeEvent& event) OVERRIDE;

  // Our X11 state.
  Display* x_display_;
  ::Window x_root_window_;

  // Input-only window used as a selection owner.
  ::Window x_window_;

  X11AtomCache atom_cache_;

  // Objects which request and receive selection data.
  SelectionRequestor clipboard_requestor_;
  SelectionRequestor primary_requestor_;

  // Temporary target map that we write to during DispatchObects.
  SelectionFormatMap clipboard_data_;

  // Objects which offer selection data to other windows.
  SelectionOwner clipboard_owner_;
  SelectionOwner primary_owner_;

  DISALLOW_COPY_AND_ASSIGN(AuraX11Details);
};

Clipboard::AuraX11Details::AuraX11Details()
    : x_display_(gfx::GetXDisplay()),
      x_root_window_(DefaultRootWindow(x_display_)),
      x_window_(XCreateWindow(
          x_display_, x_root_window_,
          -100, -100, 10, 10,  // x, y, width, height
          0,                   // border width
          CopyFromParent,      // depth
          InputOnly,
          CopyFromParent,      // visual
          0,
          NULL)),
      atom_cache_(x_display_, kAtomsToCache),
      clipboard_requestor_(x_display_, x_window_,
                           atom_cache_.GetAtom(kClipboard)),
      primary_requestor_(x_display_, x_window_, XA_PRIMARY),
      clipboard_owner_(x_display_, x_window_, atom_cache_.GetAtom(kClipboard)),
      primary_owner_(x_display_, x_window_, XA_PRIMARY) {
  // We don't know all possible MIME types at compile time.
  atom_cache_.allow_uncached_atoms();

  XStoreName(x_display_, x_window_, "Chromium clipboard");
  XSelectInput(x_display_, x_window_, PropertyChangeMask);

  base::MessagePumpX11::Current()->AddDispatcherForWindow(this, x_window_);
}

Clipboard::AuraX11Details::~AuraX11Details() {
  base::MessagePumpX11::Current()->RemoveDispatcherForWindow(x_window_);

  XDestroyWindow(x_display_, x_window_);
}

::Atom Clipboard::AuraX11Details::LookupSelectionForClipboardType(
    ClipboardType type) const {
  if (type == CLIPBOARD_TYPE_COPY_PASTE)
    return atom_cache_.GetAtom(kClipboard);

  return XA_PRIMARY;
}

const SelectionFormatMap& Clipboard::AuraX11Details::LookupStorageForAtom(
    ::Atom atom) {
  if (atom == XA_PRIMARY)
    return primary_owner_.selection_format_map();

  DCHECK_EQ(atom_cache_.GetAtom(kClipboard), atom);
  return clipboard_owner_.selection_format_map();
}

ui::SelectionRequestor*
Clipboard::AuraX11Details::GetSelectionRequestorForClipboardType(
    ClipboardType type) {
  if (type == CLIPBOARD_TYPE_COPY_PASTE)
    return &clipboard_requestor_;
  else
    return &primary_requestor_;
}

void Clipboard::AuraX11Details::CreateNewClipboardData() {
  clipboard_data_ = SelectionFormatMap();
}

void Clipboard::AuraX11Details::InsertMapping(
    const std::string& key,
    const scoped_refptr<base::RefCountedMemory>& memory) {
  ::Atom atom_key = atom_cache_.GetAtom(key.c_str());
  clipboard_data_.Insert(atom_key, memory);
}

void Clipboard::AuraX11Details::TakeOwnershipOfSelection(ClipboardType type) {
  if (type == CLIPBOARD_TYPE_COPY_PASTE)
    return clipboard_owner_.TakeOwnershipOfSelection(clipboard_data_);
  else
    return primary_owner_.TakeOwnershipOfSelection(clipboard_data_);
}

SelectionData Clipboard::AuraX11Details::RequestAndWaitForTypes(
    ClipboardType type,
    const std::vector< ::Atom>& types) {
  ::Atom selection_name = LookupSelectionForClipboardType(type);
  if (XGetSelectionOwner(x_display_, selection_name) == x_window_) {
    // We can local fastpath instead of playing the nested message loop game
    // with the X server.
    const SelectionFormatMap& format_map = LookupStorageForAtom(selection_name);

    for (std::vector< ::Atom>::const_iterator it = types.begin();
         it != types.end(); ++it) {
      SelectionFormatMap::const_iterator format_map_it = format_map.find(*it);
      if (format_map_it != format_map.end())
        return SelectionData(format_map_it->first, format_map_it->second);
    }
  } else {
    TargetList targets = WaitAndGetTargetsList(type);
    SelectionRequestor* receiver = GetSelectionRequestorForClipboardType(type);

    std::vector< ::Atom> intersection;
    ui::GetAtomIntersection(types, targets.target_list(), &intersection);
    return receiver->RequestAndWaitForTypes(intersection);
  }

  return SelectionData();
}

TargetList Clipboard::AuraX11Details::WaitAndGetTargetsList(
    ClipboardType type) {
  ::Atom selection_name = LookupSelectionForClipboardType(type);
  std::vector< ::Atom> out;
  if (XGetSelectionOwner(x_display_, selection_name) == x_window_) {
    // We can local fastpath and return the list of local targets.
    const SelectionFormatMap& format_map = LookupStorageForAtom(selection_name);

    for (SelectionFormatMap::const_iterator it = format_map.begin();
         it != format_map.end(); ++it) {
      out.push_back(it->first);
    }
  } else {
    scoped_refptr<base::RefCountedMemory> data;
    size_t out_data_items = 0;
    ::Atom out_type = None;

    SelectionRequestor* receiver = GetSelectionRequestorForClipboardType(type);
    if (receiver->PerformBlockingConvertSelection(atom_cache_.GetAtom(kTargets),
                                                  &data,
                                                  NULL,
                                                  &out_data_items,
                                                  &out_type)) {
      if (out_type == XA_ATOM) {
        const ::Atom* atom_array =
            reinterpret_cast<const ::Atom*>(data->front());
        for (size_t i = 0; i < out_data_items; ++i)
          out.push_back(atom_array[i]);
      }
    } else {
      // There was no target list. Most Java apps doesn't offer a TARGETS list,
      // even though they AWT to. They will offer individual text types if you
      // ask. If this is the case we attempt to make sense of the contents as
      // text. This is pretty unfortunate since it means we have to actually
      // copy the data to see if it is available, but at least this path
      // shouldn't be hit for conforming programs.
      std::vector< ::Atom> types = GetTextAtoms();
      for (std::vector< ::Atom>::const_iterator it = types.begin();
           it != types.end(); ++it) {
        ::Atom type = None;
        if (receiver->PerformBlockingConvertSelection(*it,
                                                      NULL,
                                                      NULL,
                                                      NULL,
                                                      &type) &&
            type == *it) {
          out.push_back(*it);
        }
      }
    }
  }

  return TargetList(out, &atom_cache_);
}

std::vector< ::Atom> Clipboard::AuraX11Details::GetTextAtoms() const {
  return GetTextAtomsFrom(&atom_cache_);
}

std::vector< ::Atom> Clipboard::AuraX11Details::GetAtomsForFormat(
    const Clipboard::FormatType& format) {
  std::vector< ::Atom> atoms;
  atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str()));
  return atoms;
}

void Clipboard::AuraX11Details::Clear(ClipboardType type) {
  if (type == CLIPBOARD_TYPE_COPY_PASTE)
    clipboard_owner_.ClearSelectionOwner();
  else
    primary_owner_.ClearSelectionOwner();
}

uint32_t Clipboard::AuraX11Details::Dispatch(const base::NativeEvent& event) {
  XEvent* xev = event;

  switch (xev->type) {
    case SelectionRequest: {
      if (xev->xselectionrequest.selection == XA_PRIMARY)
        primary_owner_.OnSelectionRequest(xev->xselectionrequest);
      else
        clipboard_owner_.OnSelectionRequest(xev->xselectionrequest);
      break;
    }
    case SelectionNotify: {
      if (xev->xselection.selection == XA_PRIMARY)
        primary_requestor_.OnSelectionNotify(xev->xselection);
      else
        clipboard_requestor_.OnSelectionNotify(xev->xselection);
      break;
    }
    case SelectionClear: {
      if (xev->xselectionclear.selection == XA_PRIMARY)
        primary_owner_.OnSelectionClear(xev->xselectionclear);
      else
        clipboard_owner_.OnSelectionClear(xev->xselectionclear);
      break;
    }
    default:
      break;
  }

  return POST_DISPATCH_NONE;
}

///////////////////////////////////////////////////////////////////////////////
// Clipboard

Clipboard::Clipboard()
    : aurax11_details_(new AuraX11Details) {
  DCHECK(CalledOnValidThread());
}

Clipboard::~Clipboard() {
  DCHECK(CalledOnValidThread());

  // TODO(erg): We need to do whatever the equivalent of
  // gtk_clipboard_store(clipboard_) is here. When we shut down, we want the
  // current selection to live on.
}

void Clipboard::WriteObjects(ClipboardType type, const ObjectMap& objects) {
  DCHECK(CalledOnValidThread());
  DCHECK(IsSupportedClipboardType(type));

  aurax11_details_->CreateNewClipboardData();
  for (ObjectMap::const_iterator iter = objects.begin();
       iter != objects.end(); ++iter) {
    DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
  }
  aurax11_details_->TakeOwnershipOfSelection(type);

  if (type == CLIPBOARD_TYPE_COPY_PASTE) {
    ObjectMap::const_iterator text_iter = objects.find(CBF_TEXT);
    if (text_iter != objects.end()) {
      aurax11_details_->CreateNewClipboardData();
      const ObjectMapParams& params_vector = text_iter->second;
      if (params_vector.size()) {
        const ObjectMapParam& char_vector = params_vector[0];
        if (char_vector.size())
          WriteText(&char_vector.front(), char_vector.size());
      }
      aurax11_details_->TakeOwnershipOfSelection(CLIPBOARD_TYPE_SELECTION);
    }
  }
}

bool Clipboard::IsFormatAvailable(const FormatType& format,
                                  ClipboardType type) const {
  DCHECK(CalledOnValidThread());
  DCHECK(IsSupportedClipboardType(type));

  TargetList target_list = aurax11_details_->WaitAndGetTargetsList(type);
  if (format.Equals(GetPlainTextFormatType()) ||
      format.Equals(GetUrlFormatType())) {
    return target_list.ContainsText();
  }
  return target_list.ContainsFormat(format);
}

void Clipboard::Clear(ClipboardType type) {
  DCHECK(CalledOnValidThread());
  DCHECK(IsSupportedClipboardType(type));
  aurax11_details_->Clear(type);
}

void Clipboard::ReadAvailableTypes(ClipboardType type,
                                   std::vector<base::string16>* types,
                                   bool* contains_filenames) const {
  DCHECK(CalledOnValidThread());
  if (!types || !contains_filenames) {
    NOTREACHED();
    return;
  }

  TargetList target_list = aurax11_details_->WaitAndGetTargetsList(type);

  types->clear();

  if (target_list.ContainsText())
    types->push_back(base::UTF8ToUTF16(kMimeTypeText));
  if (target_list.ContainsFormat(GetHtmlFormatType()))
    types->push_back(base::UTF8ToUTF16(kMimeTypeHTML));
  if (target_list.ContainsFormat(GetRtfFormatType()))
    types->push_back(base::UTF8ToUTF16(kMimeTypeRTF));
  if (target_list.ContainsFormat(GetBitmapFormatType()))
    types->push_back(base::UTF8ToUTF16(kMimeTypePNG));
  *contains_filenames = false;

  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
      type, aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType())));
  if (data.IsValid())
    ReadCustomDataTypes(data.GetData(), data.GetSize(), types);
}

void Clipboard::ReadText(ClipboardType type, base::string16* result) const {
  DCHECK(CalledOnValidThread());

  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
      type, aurax11_details_->GetTextAtoms()));
  if (data.IsValid()) {
    std::string text = data.GetText();
    *result = base::UTF8ToUTF16(text);
  }
}

void Clipboard::ReadAsciiText(ClipboardType type, std::string* result) const {
  DCHECK(CalledOnValidThread());

  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
      type, aurax11_details_->GetTextAtoms()));
  if (data.IsValid())
    result->assign(data.GetText());
}

// TODO(estade): handle different charsets.
// TODO(port): set *src_url.
void Clipboard::ReadHTML(ClipboardType type,
                         base::string16* markup,
                         std::string* src_url,
                         uint32* fragment_start,
                         uint32* fragment_end) const {
  DCHECK(CalledOnValidThread());
  markup->clear();
  if (src_url)
    src_url->clear();
  *fragment_start = 0;
  *fragment_end = 0;

  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
      type, aurax11_details_->GetAtomsForFormat(GetHtmlFormatType())));
  if (data.IsValid()) {
    *markup = data.GetHtml();

    *fragment_start = 0;
    DCHECK(markup->length() <= kuint32max);
    *fragment_end = static_cast<uint32>(markup->length());
  }
}

void Clipboard::ReadRTF(ClipboardType type, std::string* result) const {
  DCHECK(CalledOnValidThread());

  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
      type, aurax11_details_->GetAtomsForFormat(GetRtfFormatType())));
  if (data.IsValid())
    data.AssignTo(result);
}

SkBitmap Clipboard::ReadImage(ClipboardType type) const {
  DCHECK(CalledOnValidThread());

  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
      type, aurax11_details_->GetAtomsForFormat(GetBitmapFormatType())));
  if (data.IsValid()) {
    SkBitmap bitmap;
    if (gfx::PNGCodec::Decode(data.GetData(), data.GetSize(), &bitmap))
      return SkBitmap(bitmap);
  }

  return SkBitmap();
}

void Clipboard::ReadCustomData(ClipboardType clipboard_type,
                               const base::string16& type,
                               base::string16* result) const {
  DCHECK(CalledOnValidThread());

  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
      clipboard_type,
      aurax11_details_->GetAtomsForFormat(GetWebCustomDataFormatType())));
  if (data.IsValid())
    ReadCustomDataForType(data.GetData(), data.GetSize(), type, result);
}

void Clipboard::ReadBookmark(base::string16* title, std::string* url) const {
  DCHECK(CalledOnValidThread());
  // TODO(erg): This was left NOTIMPLEMENTED() in the gtk port too.
  NOTIMPLEMENTED();
}

void Clipboard::ReadData(const FormatType& format, std::string* result) const {
  DCHECK(CalledOnValidThread());

  SelectionData data(aurax11_details_->RequestAndWaitForTypes(
      CLIPBOARD_TYPE_COPY_PASTE, aurax11_details_->GetAtomsForFormat(format)));
  if (data.IsValid())
    data.AssignTo(result);
}

uint64 Clipboard::GetSequenceNumber(ClipboardType type) {
  DCHECK(CalledOnValidThread());
  if (type == CLIPBOARD_TYPE_COPY_PASTE)
    return SelectionChangeObserver::GetInstance()->clipboard_sequence_number();
  else
    return SelectionChangeObserver::GetInstance()->primary_sequence_number();
}

void Clipboard::WriteText(const char* text_data, size_t text_len) {
  std::string str(text_data, text_len);
  scoped_refptr<base::RefCountedMemory> mem(
      base::RefCountedString::TakeString(&str));

  aurax11_details_->InsertMapping(kMimeTypeText, mem);
  aurax11_details_->InsertMapping(kText, mem);
  aurax11_details_->InsertMapping(kString, mem);
  aurax11_details_->InsertMapping(kUtf8String, mem);
}

void Clipboard::WriteHTML(const char* markup_data,
                          size_t markup_len,
                          const char* url_data,
                          size_t url_len) {
  // TODO(estade): We need to expand relative links with |url_data|.
  static const char* html_prefix = "<meta http-equiv=\"content-type\" "
                                   "content=\"text/html; charset=utf-8\">";
  std::string data = html_prefix;
  data += std::string(markup_data, markup_len);
  // Some programs expect NULL-terminated data. See http://crbug.com/42624
  data += '\0';

  scoped_refptr<base::RefCountedMemory> mem(
      base::RefCountedString::TakeString(&data));
  aurax11_details_->InsertMapping(kMimeTypeHTML, mem);
}

void Clipboard::WriteRTF(const char* rtf_data, size_t data_len) {
  WriteData(GetRtfFormatType(), rtf_data, data_len);
}

void Clipboard::WriteBookmark(const char* title_data,
                              size_t title_len,
                              const char* url_data,
                              size_t url_len) {
  // Write as a mozilla url (UTF16: URL, newline, title).
  base::string16 url = base::UTF8ToUTF16(std::string(url_data, url_len) + "\n");
  base::string16 title = base::UTF8ToUTF16(std::string(title_data, title_len));

  std::vector<unsigned char> data;
  ui::AddString16ToVector(url, &data);
  ui::AddString16ToVector(title, &data);
  scoped_refptr<base::RefCountedMemory> mem(
      base::RefCountedBytes::TakeVector(&data));

  aurax11_details_->InsertMapping(kMimeTypeMozillaURL, mem);
}

// Write an extra flavor that signifies WebKit was the last to modify the
// pasteboard. This flavor has no data.
void Clipboard::WriteWebSmartPaste() {
  std::string empty;
  aurax11_details_->InsertMapping(
      kMimeTypeWebkitSmartPaste,
      scoped_refptr<base::RefCountedMemory>(
          base::RefCountedString::TakeString(&empty)));
}

void Clipboard::WriteBitmap(const SkBitmap& bitmap) {
  // Encode the bitmap as a PNG for transport.
  std::vector<unsigned char> output;
  if (gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, false, &output)) {
    aurax11_details_->InsertMapping(kMimeTypePNG,
                                    base::RefCountedBytes::TakeVector(
                                        &output));
  }
}

void Clipboard::WriteData(const FormatType& format,
                          const char* data_data,
                          size_t data_len) {
  // We assume that certain mapping types are only written by trusted code.
  // Therefore we must upkeep their integrity.
  if (format.Equals(GetBitmapFormatType()))
    return;

  std::vector<unsigned char> bytes(data_data, data_data + data_len);
  scoped_refptr<base::RefCountedMemory> mem(
      base::RefCountedBytes::TakeVector(&bytes));
  aurax11_details_->InsertMapping(format.ToString(), mem);
}

// static
Clipboard::FormatType Clipboard::GetFormatType(
    const std::string& format_string) {
  return FormatType::Deserialize(format_string);
}

// static
const Clipboard::FormatType& Clipboard::GetUrlFormatType() {
  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeURIList));
  return type;
}

// static
const Clipboard::FormatType& Clipboard::GetUrlWFormatType() {
  return GetUrlFormatType();
}

// static
const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() {
  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeText));
  return type;
}

// static
const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() {
  return GetPlainTextFormatType();
}

// static
const Clipboard::FormatType& Clipboard::GetFilenameFormatType() {
  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeFilename));
  return type;
}

// static
const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() {
  return Clipboard::GetFilenameFormatType();
}

// static
const Clipboard::FormatType& Clipboard::GetHtmlFormatType() {
  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeHTML));
  return type;
}

// static
const Clipboard::FormatType& Clipboard::GetRtfFormatType() {
  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeRTF));
  return type;
}

// static
const Clipboard::FormatType& Clipboard::GetBitmapFormatType() {
  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePNG));
  return type;
}

// static
const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() {
  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebkitSmartPaste));
  return type;
}

// static
const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() {
  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData));
  return type;
}

// static
const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() {
  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData));
  return type;
}

}  // namespace ui

/* [<][>][^][v][top][bottom][index][help] */