| // 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_aurax11.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/metrics/histogram.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/events/platform/platform_event_dispatcher.h" |
| #include "ui/events/platform/platform_event_observer.h" |
| #include "ui/events/platform/platform_event_source.h" |
| #include "ui/gfx/codec/png_codec.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/x/x11_atom_cache.h" |
| |
| namespace ui { |
| |
| namespace { |
| |
| const char kClipboard[] = "CLIPBOARD"; |
| const char kClipboardManager[] = "CLIPBOARD_MANAGER"; |
| const char kMimeTypeFilename[] = "chromium/filename"; |
| const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data"; |
| const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste"; |
| const char kSaveTargets[] = "SAVE_TARGETS"; |
| const char kTargets[] = "TARGETS"; |
| |
| const char* kAtomsToCache[] = { |
| kClipboard, |
| kClipboardManager, |
| Clipboard::kMimeTypePNG, |
| kMimeTypeFilename, |
| kMimeTypeMozillaURL, |
| kMimeTypeWebkitSmartPaste, |
| kSaveTargets, |
| kString, |
| kTargets, |
| kText, |
| kUtf8String, |
| NULL |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| // Uses the XFixes API to provide sequence numbers for GetSequenceNumber(). |
| class SelectionChangeObserver : public ui::PlatformEventObserver { |
| 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(); |
| ~SelectionChangeObserver() override; |
| |
| // ui::PlatformEventObserver: |
| void WillProcessEvent(const ui::PlatformEvent& event) override; |
| void DidProcessEvent(const ui::PlatformEvent& 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); |
| |
| ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this); |
| } |
| } |
| |
| SelectionChangeObserver::~SelectionChangeObserver() { |
| // We are a singleton; we will outlive the event source. |
| } |
| |
| SelectionChangeObserver* SelectionChangeObserver::GetInstance() { |
| return Singleton<SelectionChangeObserver>::get(); |
| } |
| |
| void SelectionChangeObserver::WillProcessEvent(const ui::PlatformEvent& 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; |
| } |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| // 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_; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // ClipboardAuraX11::AuraX11Details |
| |
| // Private implementation of our X11 integration. Keeps X11 headers out of the |
| // majority of chrome, which break badly. |
| class ClipboardAuraX11::AuraX11Details : public PlatformEventDispatcher { |
| public: |
| AuraX11Details(); |
| ~AuraX11Details() override; |
| |
| 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 X11 type that we pass to various XSelection functions for |
| // CLIPBOARD_TYPE_COPY_PASTE. |
| ::Atom GetCopyPasteSelection() const; |
| |
| // 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); |
| |
| // If we own the CLIPBOARD selection, requests the clipboard manager to take |
| // ownership of it. |
| void StoreCopyPasteDataAndWait(); |
| |
| private: |
| // PlatformEventDispatcher: |
| bool CanDispatchEvent(const PlatformEvent& event) override; |
| uint32_t DispatchEvent(const PlatformEvent& 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_; |
| |
| // Object which requests and receives selection data. |
| SelectionRequestor selection_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); |
| }; |
| |
| ClipboardAuraX11::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), |
| selection_requestor_(x_display_, x_window_, this), |
| 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); |
| |
| if (PlatformEventSource::GetInstance()) |
| PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); |
| } |
| |
| ClipboardAuraX11::AuraX11Details::~AuraX11Details() { |
| if (PlatformEventSource::GetInstance()) |
| PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); |
| |
| XDestroyWindow(x_display_, x_window_); |
| } |
| |
| ::Atom ClipboardAuraX11::AuraX11Details::LookupSelectionForClipboardType( |
| ClipboardType type) const { |
| if (type == CLIPBOARD_TYPE_COPY_PASTE) |
| return GetCopyPasteSelection(); |
| |
| return XA_PRIMARY; |
| } |
| |
| ::Atom ClipboardAuraX11::AuraX11Details::GetCopyPasteSelection() const { |
| return atom_cache_.GetAtom(kClipboard); |
| } |
| |
| const SelectionFormatMap& |
| ClipboardAuraX11::AuraX11Details::LookupStorageForAtom(::Atom atom) { |
| if (atom == XA_PRIMARY) |
| return primary_owner_.selection_format_map(); |
| |
| DCHECK_EQ(GetCopyPasteSelection(), atom); |
| return clipboard_owner_.selection_format_map(); |
| } |
| |
| void ClipboardAuraX11::AuraX11Details::CreateNewClipboardData() { |
| clipboard_data_ = SelectionFormatMap(); |
| } |
| |
| void ClipboardAuraX11::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 ClipboardAuraX11::AuraX11Details::TakeOwnershipOfSelection( |
| ClipboardType type) { |
| if (type == CLIPBOARD_TYPE_COPY_PASTE) |
| return clipboard_owner_.TakeOwnershipOfSelection(clipboard_data_); |
| else |
| return primary_owner_.TakeOwnershipOfSelection(clipboard_data_); |
| } |
| |
| SelectionData ClipboardAuraX11::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); |
| |
| ::Atom selection_name = LookupSelectionForClipboardType(type); |
| std::vector< ::Atom> intersection; |
| ui::GetAtomIntersection(types, targets.target_list(), &intersection); |
| return selection_requestor_.RequestAndWaitForTypes(selection_name, |
| intersection); |
| } |
| |
| return SelectionData(); |
| } |
| |
| TargetList ClipboardAuraX11::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; |
| |
| if (selection_requestor_.PerformBlockingConvertSelection( |
| selection_name, |
| atom_cache_.GetAtom(kTargets), |
| &data, |
| &out_data_items, |
| &out_type)) { |
| // Some apps return an |out_type| of "TARGETS". (crbug.com/377893) |
| if (out_type == XA_ATOM || out_type == atom_cache_.GetAtom(kTargets)) { |
| 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 (selection_requestor_.PerformBlockingConvertSelection(selection_name, |
| *it, |
| NULL, |
| NULL, |
| &type) && |
| type == *it) { |
| out.push_back(*it); |
| } |
| } |
| } |
| } |
| |
| return TargetList(out, &atom_cache_); |
| } |
| |
| std::vector<::Atom> ClipboardAuraX11::AuraX11Details::GetTextAtoms() const { |
| return GetTextAtomsFrom(&atom_cache_); |
| } |
| |
| std::vector<::Atom> ClipboardAuraX11::AuraX11Details::GetAtomsForFormat( |
| const Clipboard::FormatType& format) { |
| std::vector< ::Atom> atoms; |
| atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str())); |
| return atoms; |
| } |
| |
| void ClipboardAuraX11::AuraX11Details::Clear(ClipboardType type) { |
| if (type == CLIPBOARD_TYPE_COPY_PASTE) |
| clipboard_owner_.ClearSelectionOwner(); |
| else |
| primary_owner_.ClearSelectionOwner(); |
| } |
| |
| void ClipboardAuraX11::AuraX11Details::StoreCopyPasteDataAndWait() { |
| ::Atom selection = GetCopyPasteSelection(); |
| if (XGetSelectionOwner(x_display_, selection) != x_window_) |
| return; |
| |
| ::Atom clipboard_manager_atom = atom_cache_.GetAtom(kClipboardManager); |
| if (XGetSelectionOwner(x_display_, clipboard_manager_atom) == None) |
| return; |
| |
| const SelectionFormatMap& format_map = LookupStorageForAtom(selection); |
| if (format_map.size() == 0) |
| return; |
| std::vector<Atom> targets = format_map.GetTypes(); |
| |
| base::TimeTicks start = base::TimeTicks::Now(); |
| selection_requestor_.PerformBlockingConvertSelectionWithParameter( |
| atom_cache_.GetAtom(kClipboardManager), |
| atom_cache_.GetAtom(kSaveTargets), |
| targets); |
| UMA_HISTOGRAM_TIMES("Clipboard.X11StoreCopyPasteDuration", |
| base::TimeTicks::Now() - start); |
| } |
| |
| bool ClipboardAuraX11::AuraX11Details::CanDispatchEvent( |
| const PlatformEvent& event) { |
| if (event->xany.window == x_window_) |
| return true; |
| |
| if (event->type == PropertyNotify) { |
| return primary_owner_.CanDispatchPropertyEvent(*event) || |
| clipboard_owner_.CanDispatchPropertyEvent(*event) || |
| selection_requestor_.CanDispatchPropertyEvent(*event); |
| } |
| return false; |
| } |
| |
| uint32_t ClipboardAuraX11::AuraX11Details::DispatchEvent( |
| const PlatformEvent& xev) { |
| switch (xev->type) { |
| case SelectionRequest: { |
| if (xev->xselectionrequest.selection == XA_PRIMARY) { |
| primary_owner_.OnSelectionRequest(*xev); |
| } else { |
| // We should not get requests for the CLIPBOARD_MANAGER selection |
| // because we never take ownership of it. |
| DCHECK_EQ(GetCopyPasteSelection(), xev->xselectionrequest.selection); |
| clipboard_owner_.OnSelectionRequest(*xev); |
| } |
| break; |
| } |
| case SelectionNotify: { |
| selection_requestor_.OnSelectionNotify(*xev); |
| break; |
| } |
| case SelectionClear: { |
| if (xev->xselectionclear.selection == XA_PRIMARY) { |
| primary_owner_.OnSelectionClear(*xev); |
| } else { |
| // We should not get requests for the CLIPBOARD_MANAGER selection |
| // because we never take ownership of it. |
| DCHECK_EQ(GetCopyPasteSelection(), xev->xselection.selection); |
| clipboard_owner_.OnSelectionClear(*xev); |
| } |
| break; |
| } |
| case PropertyNotify: { |
| if (primary_owner_.CanDispatchPropertyEvent(*xev)) |
| primary_owner_.OnPropertyEvent(*xev); |
| if (clipboard_owner_.CanDispatchPropertyEvent(*xev)) |
| clipboard_owner_.OnPropertyEvent(*xev); |
| if (selection_requestor_.CanDispatchPropertyEvent(*xev)) |
| selection_requestor_.OnPropertyEvent(*xev); |
| break; |
| } |
| default: |
| break; |
| } |
| |
| return POST_DISPATCH_NONE; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Various predefined FormatTypes. |
| // 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; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Clipboard factory method. |
| Clipboard* Clipboard::Create() { |
| return new ClipboardAuraX11; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // ClipboardAuraX11 |
| |
| ClipboardAuraX11::ClipboardAuraX11() : aurax11_details_(new AuraX11Details) { |
| DCHECK(CalledOnValidThread()); |
| } |
| |
| ClipboardAuraX11::~ClipboardAuraX11() { |
| DCHECK(CalledOnValidThread()); |
| |
| aurax11_details_->StoreCopyPasteDataAndWait(); |
| } |
| |
| uint64 ClipboardAuraX11::GetSequenceNumber(ClipboardType type) const { |
| DCHECK(CalledOnValidThread()); |
| if (type == CLIPBOARD_TYPE_COPY_PASTE) |
| return SelectionChangeObserver::GetInstance()->clipboard_sequence_number(); |
| else |
| return SelectionChangeObserver::GetInstance()->primary_sequence_number(); |
| } |
| |
| bool ClipboardAuraX11::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 ClipboardAuraX11::Clear(ClipboardType type) { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(IsSupportedClipboardType(type)); |
| aurax11_details_->Clear(type); |
| } |
| |
| void ClipboardAuraX11::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 ClipboardAuraX11::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 ClipboardAuraX11::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 ClipboardAuraX11::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 ClipboardAuraX11::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 ClipboardAuraX11::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 ClipboardAuraX11::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 ClipboardAuraX11::ReadBookmark(base::string16* title, |
| std::string* url) const { |
| DCHECK(CalledOnValidThread()); |
| // TODO(erg): This was left NOTIMPLEMENTED() in the gtk port too. |
| NOTIMPLEMENTED(); |
| } |
| |
| void ClipboardAuraX11::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); |
| } |
| |
| void ClipboardAuraX11::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); |
| } |
| } |
| } |
| |
| void ClipboardAuraX11::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 ClipboardAuraX11::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 ClipboardAuraX11::WriteRTF(const char* rtf_data, size_t data_len) { |
| WriteData(GetRtfFormatType(), rtf_data, data_len); |
| } |
| |
| void ClipboardAuraX11::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(base::StringPiece(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 ClipboardAuraX11::WriteWebSmartPaste() { |
| std::string empty; |
| aurax11_details_->InsertMapping( |
| kMimeTypeWebkitSmartPaste, |
| scoped_refptr<base::RefCountedMemory>( |
| base::RefCountedString::TakeString(&empty))); |
| } |
| |
| void ClipboardAuraX11::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 ClipboardAuraX11::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); |
| } |
| |
| } // namespace ui |