summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ui/base/clipboard/clipboard_aurax11.cc445
-rw-r--r--ui/base/x/selection_owner.cc122
-rw-r--r--ui/base/x/selection_owner.h70
-rw-r--r--ui/base/x/selection_requestor.cc139
-rw-r--r--ui/base/x/selection_requestor.h81
-rw-r--r--ui/base/x/selection_utils.cc44
-rw-r--r--ui/base/x/selection_utils.h57
-rw-r--r--ui/ui.gyp12
8 files changed, 619 insertions, 351 deletions
diff --git a/ui/base/clipboard/clipboard_aurax11.cc b/ui/base/clipboard/clipboard_aurax11.cc
index 44cbe42..e712224 100644
--- a/ui/base/clipboard/clipboard_aurax11.cc
+++ b/ui/base/clipboard/clipboard_aurax11.cc
@@ -17,11 +17,13 @@
#include "base/memory/singleton.h"
#include "base/message_pump_aurax11.h"
#include "base/message_pump_observer.h"
-#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/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_atom_cache.h"
#include "ui/base/x/x11_util.h"
@@ -31,14 +33,12 @@ namespace ui {
namespace {
-const char kChromeSelection[] = "CHROME_SELECTION";
const char kClipboard[] = "CLIPBOARD";
const char kMimeTypeBitmap[] = "image/bmp";
const char kMimeTypeFilename[] = "chromium/filename";
const char kMimeTypeMozillaURL[] = "text/x-moz-url";
const char kMimeTypePepperCustomData[] = "chromium/x-pepper-custom-data";
const char kMimeTypeWebkitSmartPaste[] = "chromium/x-webkit-paste";
-const char kMultiple[] = "MULTIPLE";
const char kSourceTagType[] = "org.chromium.source-tag";
const char kString[] = "STRING";
const char kTargets[] = "TARGETS";
@@ -46,13 +46,11 @@ const char kText[] = "TEXT";
const char kUtf8String[] = "UTF8_STRING";
const char* kAtomsToCache[] = {
- kChromeSelection,
kClipboard,
kMimeTypeBitmap,
kMimeTypeFilename,
kMimeTypeMozillaURL,
kMimeTypeWebkitSmartPaste,
- kMultiple,
kSourceTagType,
kString,
kTargets,
@@ -63,17 +61,6 @@ const char* kAtomsToCache[] = {
///////////////////////////////////////////////////////////////////////////////
-// Returns a list of all text atoms that we handle.
-std::vector< ::Atom> GetTextAtomsFrom(const X11AtomCache* atom_cache) {
- std::vector< ::Atom> atoms;
- atoms.push_back(atom_cache->GetAtom(kUtf8String));
- atoms.push_back(atom_cache->GetAtom(kString));
- atoms.push_back(atom_cache->GetAtom(kText));
- return atoms;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
// Uses the XFixes API to provide sequence numbers for GetSequenceNumber().
class SelectionChangeObserver : public base::MessagePumpObserver {
public:
@@ -156,56 +143,6 @@ base::EventStatus SelectionChangeObserver::WillProcessEvent(
///////////////////////////////////////////////////////////////////////////////
-// Represents the selection in different data formats. Binary data passed in is
-// assumed to be allocated with new char[], and is owned by FormatMap.
-class FormatMap {
- public:
- // Our internal data store, which we only expose through iterators.
- typedef std::map< ::Atom, std::pair<char*, size_t> > InternalMap;
- typedef std::map< ::Atom, std::pair<char*, size_t> >::const_iterator
- const_iterator;
-
- FormatMap();
- ~FormatMap();
-
- // Adds the selection in the format |atom|. Ownership of |data| is passed to
- // us.
- void Insert(::Atom atom, char* data, size_t size);
-
- // Pass through to STL map. Only allow non-mutation access.
- const_iterator begin() const { return data_.begin(); }
- const_iterator end() const { return data_.end(); }
- const_iterator find(::Atom atom) const { return data_.find(atom); }
- size_t size() const { return data_.size(); }
-
- private:
- InternalMap data_;
-
- DISALLOW_COPY_AND_ASSIGN(FormatMap);
-};
-
-FormatMap::FormatMap() {}
-
-FormatMap::~FormatMap() {
- // WriteText() inserts the same pointer multiple times for different
- // representations; we need to dedupe it.
- std::set<char*> to_delete;
- for (InternalMap::iterator it = data_.begin(); it != data_.end(); ++it)
- to_delete.insert(it->second.first);
-
- for (std::set<char*>::iterator it = to_delete.begin(); it != to_delete.end();
- ++it) {
- delete [] *it;
- }
-}
-
-void FormatMap::Insert(::Atom atom, char* data, size_t size) {
- DCHECK(data_.find(atom) == data_.end());
- data_.insert(std::make_pair(atom, std::make_pair(data, size)));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
// Represents a list of possible return types. Copy constructable.
class TargetList {
public:
@@ -374,8 +311,11 @@ class Clipboard::AuraX11Details : public base::MessagePumpDispatcher {
// given buffer.
::Atom LookupSelectionForBuffer(Buffer buffer) const;
- // Finds the FormatMap for the incoming selection atom.
- FormatMap* LookupStorageForAtom(::Atom atom);
+ // Returns the object which is responsible for communication on |buffer|.
+ SelectionRequestor* GetSelectionRequestorForBuffer(Buffer buffer);
+
+ // Finds the SelectionFormatMap for the incoming selection atom.
+ 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
@@ -408,18 +348,6 @@ class Clipboard::AuraX11Details : public base::MessagePumpDispatcher {
// blocking message loop.
TargetList WaitAndGetTargetsList(Buffer buffer);
- // Does the work of requesting |target| from |selection_name|, spinning up
- // the nested message loop, and reading the resulting data back. |out_data|
- // is allocated with the X allocator and must be freed with
- // XFree(). |out_data_bytes| is the length in machine chars, while
- // |out_data_items| is the length in |out_type| items.
- bool PerformBlockingConvertSelection(::Atom selection_name,
- ::Atom target,
- unsigned char** out_data,
- size_t* out_data_bytes,
- size_t* out_data_items,
- ::Atom* out_type);
-
// Returns a list of all text atoms that we handle.
std::vector< ::Atom> GetTextAtoms() const;
@@ -430,23 +358,9 @@ class Clipboard::AuraX11Details : public base::MessagePumpDispatcher {
void Clear(Buffer buffer);
private:
- // Called by Dispatch to handle specific types of events.
- void HandleSelectionRequest(const XSelectionRequestEvent& event);
- void HandleSelectionNotify(const XSelectionEvent& event);
- void HandleSelectionClear(const XSelectionClearEvent& event);
- void HandlePropertyNotify(const XPropertyEvent& event);
-
// Overridden from base::MessagePumpDispatcher:
virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
- // Temporary target map that we write to during DispatchObects.
- scoped_ptr<FormatMap> clipboard_data_;
-
- // The current value of our clipboard and primary selections. These should be
- // non-NULL when we own the selection.
- scoped_ptr<FormatMap> clipboard_selection_;
- scoped_ptr<FormatMap> primary_selection_;
-
// Our X11 state.
Display* x_display_;
::Window x_root_window_;
@@ -454,25 +368,18 @@ class Clipboard::AuraX11Details : public base::MessagePumpDispatcher {
// Input-only window used as a selection owner.
::Window x_window_;
- // True if we're currently running a nested message loop, waiting for data to
- // come back from the X server.
- bool in_nested_loop_;
+ X11AtomCache atom_cache_;
- // Data to the current XConvertSelection request. Used for error detection;
- // we verify it on the return message.
- ::Atom current_selection_;
- ::Atom current_target_;
+ // Objects which request and receive selection data.
+ SelectionRequestor clipboard_requestor_;
+ SelectionRequestor primary_requestor_;
- // The property in the returning SelectNotify message is used to signal
- // success. If None, our request failed somehow. If equal to the property
- // atom that we sent in the XConvertSelection call, we can read that property
- // on |x_window_| for the requested data.
- ::Atom returned_property_;
+ // Temporary target map that we write to during DispatchObects.
+ scoped_ptr<SelectionFormatMap> clipboard_data_;
- // Called to terminate the nested message loop.
- base::Closure quit_closure_;
-
- X11AtomCache atom_cache_;
+ // Objects which offer selection data to other windows.
+ SelectionOwner clipboard_owner_;
+ SelectionOwner primary_owner_;
DISALLOW_COPY_AND_ASSIGN(AuraX11Details);
};
@@ -480,20 +387,24 @@ class Clipboard::AuraX11Details : public base::MessagePumpDispatcher {
Clipboard::AuraX11Details::AuraX11Details()
: x_display_(GetXDisplay()),
x_root_window_(DefaultRootWindow(x_display_)),
- in_nested_loop_(false),
- atom_cache_(x_display_, kAtomsToCache) {
+ 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();
- 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);
XStoreName(x_display_, x_window_, "Chromium clipboard");
XSelectInput(x_display_, x_window_, PropertyChangeMask);
@@ -514,17 +425,26 @@ Clipboard::AuraX11Details::~AuraX11Details() {
return XA_PRIMARY;
}
-FormatMap* Clipboard::AuraX11Details::LookupStorageForAtom(::Atom atom) {
+SelectionFormatMap* Clipboard::AuraX11Details::LookupStorageForAtom(
+ ::Atom atom) {
if (atom == XA_PRIMARY)
- return primary_selection_.get();
+ return primary_owner_.selection_format_map();
else if (atom == atom_cache_.GetAtom(kClipboard))
- return clipboard_selection_.get();
+ return clipboard_owner_.selection_format_map();
else
return NULL;
}
+ui::SelectionRequestor*
+Clipboard::AuraX11Details::GetSelectionRequestorForBuffer(Buffer buffer) {
+ if (buffer == BUFFER_STANDARD)
+ return &clipboard_requestor_;
+ else
+ return &primary_requestor_;
+}
+
void Clipboard::AuraX11Details::CreateNewClipboardData() {
- clipboard_data_.reset(new FormatMap);
+ clipboard_data_.reset(new SelectionFormatMap);
}
void Clipboard::AuraX11Details::InsertMapping(const std::string& key,
@@ -535,17 +455,10 @@ void Clipboard::AuraX11Details::InsertMapping(const std::string& key,
}
void Clipboard::AuraX11Details::TakeOwnershipOfSelection(Buffer buffer) {
- // Tell the X server that we are now the selection owner.
- ::Atom xselection = LookupSelectionForBuffer(buffer);
- XSetSelectionOwner(x_display_, xselection, x_window_, CurrentTime);
-
- if (XGetSelectionOwner(x_display_, xselection) == x_window_) {
- // The X server agrees that we are the selection owner. Commit our data.
- if (buffer == BUFFER_STANDARD)
- clipboard_selection_ = clipboard_data_.Pass();
- else
- primary_selection_ = clipboard_data_.Pass();
- }
+ if (buffer == BUFFER_STANDARD)
+ return clipboard_owner_.TakeOwnershipOfSelection(clipboard_data_.Pass());
+ else
+ return primary_owner_.TakeOwnershipOfSelection(clipboard_data_.Pass());
}
scoped_ptr<SelectionData> Clipboard::AuraX11Details::RequestAndWaitForTypes(
@@ -555,12 +468,12 @@ scoped_ptr<SelectionData> Clipboard::AuraX11Details::RequestAndWaitForTypes(
if (XGetSelectionOwner(x_display_, selection_name) == x_window_) {
// We can local fastpath instead of playing the nested message loop game
// with the X server.
- FormatMap* format_map = LookupStorageForAtom(selection_name);
+ SelectionFormatMap* format_map = LookupStorageForAtom(selection_name);
DCHECK(format_map);
for (std::vector< ::Atom>::const_iterator it = types.begin();
it != types.end(); ++it) {
- FormatMap::const_iterator format_map_it = format_map->find(*it);
+ SelectionFormatMap::const_iterator format_map_it = format_map->find(*it);
if (format_map_it != format_map->end()) {
scoped_ptr<SelectionData> data_out(new SelectionData(&atom_cache_));
data_out->Set(format_map_it->first, format_map_it->second.first,
@@ -570,6 +483,7 @@ scoped_ptr<SelectionData> Clipboard::AuraX11Details::RequestAndWaitForTypes(
}
} else {
TargetList targets = WaitAndGetTargetsList(buffer);
+ SelectionRequestor* receiver = GetSelectionRequestorForBuffer(buffer);
for (std::vector< ::Atom>::const_iterator it = types.begin();
it != types.end(); ++it) {
@@ -577,12 +491,11 @@ scoped_ptr<SelectionData> Clipboard::AuraX11Details::RequestAndWaitForTypes(
size_t data_bytes = 0;
::Atom type = None;
if (targets.ContainsAtom(*it) &&
- PerformBlockingConvertSelection(selection_name,
- *it,
- &data,
- &data_bytes,
- NULL,
- &type) &&
+ receiver->PerformBlockingConvertSelection(*it,
+ &data,
+ &data_bytes,
+ NULL,
+ &type) &&
type == *it) {
scoped_ptr<SelectionData> data_out(new SelectionData(&atom_cache_));
data_out->Set(type, (char*)data, data_bytes, true);
@@ -600,10 +513,10 @@ TargetList Clipboard::AuraX11Details::WaitAndGetTargetsList(
std::vector< ::Atom> out;
if (XGetSelectionOwner(x_display_, selection_name) == x_window_) {
// We can local fastpath and return the list of local targets.
- FormatMap* format_map = LookupStorageForAtom(selection_name);
+ SelectionFormatMap* format_map = LookupStorageForAtom(selection_name);
DCHECK(format_map);
- for (FormatMap::const_iterator it = format_map->begin();
+ for (SelectionFormatMap::const_iterator it = format_map->begin();
it != format_map->end(); ++it) {
out.push_back(it->first);
}
@@ -612,12 +525,12 @@ TargetList Clipboard::AuraX11Details::WaitAndGetTargetsList(
size_t out_data_items = 0;
::Atom out_type = None;
- if (PerformBlockingConvertSelection(selection_name,
- atom_cache_.GetAtom(kTargets),
- &data,
- NULL,
- &out_data_items,
- &out_type)) {
+ SelectionRequestor* receiver = GetSelectionRequestorForBuffer(buffer);
+ if (receiver->PerformBlockingConvertSelection(atom_cache_.GetAtom(kTargets),
+ &data,
+ NULL,
+ &out_data_items,
+ &out_type)) {
::Atom* atom_array = reinterpret_cast< ::Atom*>(data);
for (size_t i = 0; i < out_data_items; ++i)
out.push_back(atom_array[i]);
@@ -634,12 +547,11 @@ TargetList Clipboard::AuraX11Details::WaitAndGetTargetsList(
for (std::vector< ::Atom>::const_iterator it = types.begin();
it != types.end(); ++it) {
::Atom type = None;
- if (PerformBlockingConvertSelection(selection_name,
- *it,
- NULL,
- NULL,
- NULL,
- &type) &&
+ if (receiver->PerformBlockingConvertSelection(*it,
+ NULL,
+ NULL,
+ NULL,
+ &type) &&
type == *it) {
out.push_back(*it);
}
@@ -650,91 +562,6 @@ TargetList Clipboard::AuraX11Details::WaitAndGetTargetsList(
return TargetList(out, &atom_cache_);
}
-bool Clipboard::AuraX11Details::PerformBlockingConvertSelection(
- ::Atom selection_name,
- ::Atom target,
- unsigned char** out_data,
- size_t* out_data_bytes,
- size_t* out_data_items,
- ::Atom* out_type) {
- // The name of the property we're asking to be set on |x_window_|.
- ::Atom property_to_set = atom_cache_.GetAtom(kChromeSelection);
-
- XConvertSelection(x_display_,
- selection_name,
- target,
- property_to_set,
- x_window_,
- CurrentTime);
-
- // Now that we've thrown our message off to the X11 server, we block waiting
- // for a response.
- MessageLoopForUI* loop = MessageLoopForUI::current();
- MessageLoop::ScopedNestableTaskAllower allow_nested(loop);
- base::RunLoop run_loop(base::MessagePumpAuraX11::Current());
-
- current_selection_ = selection_name;
- current_target_ = target;
- in_nested_loop_ = true;
- quit_closure_ = run_loop.QuitClosure();
- run_loop.Run();
- in_nested_loop_ = false;
- current_selection_ = None;
- current_target_ = None;
-
- if (returned_property_ != property_to_set)
- return false;
-
- // Retrieve the data from our window.
- unsigned long nitems = 0;
- unsigned long nbytes = 0;
- Atom prop_type = None;
- int prop_format = 0;
- unsigned char* property_data = NULL;
- if (XGetWindowProperty(x_display_,
- x_window_,
- returned_property_,
- 0, 0x1FFFFFFF /* MAXINT32 / 4 */, False,
- AnyPropertyType, &prop_type, &prop_format,
- &nitems, &nbytes, &property_data) != Success) {
- return false;
- }
-
- if (prop_type == None)
- return false;
-
- if (out_data)
- *out_data = property_data;
-
- if (out_data_bytes) {
- // So even though we should theoretically have nbytes (and we can't
- // pass NULL there), we need to manually calculate the byte length here
- // because nbytes always returns zero.
- switch (prop_format) {
- case 8:
- *out_data_bytes = nitems;
- break;
- case 16:
- *out_data_bytes = sizeof(short) * nitems;
- break;
- case 32:
- *out_data_bytes = sizeof(long) * nitems;
- break;
- default:
- NOTREACHED();
- break;
- }
- }
-
- if (out_data_items)
- *out_data_items = nitems;
-
- if (out_type)
- *out_type = prop_type;
-
- return true;
-}
-
std::vector< ::Atom> Clipboard::AuraX11Details::GetTextAtoms() const {
return GetTextAtomsFrom(&atom_cache_);
}
@@ -747,123 +574,37 @@ std::vector< ::Atom> Clipboard::AuraX11Details::GetAtomsForFormat(
}
void Clipboard::AuraX11Details::Clear(Buffer buffer) {
- ::Atom selection_name = LookupSelectionForBuffer(buffer);
- if (XGetSelectionOwner(x_display_, selection_name) == x_window_)
- XSetSelectionOwner(x_display_, selection_name, None, CurrentTime);
-
if (buffer == BUFFER_STANDARD)
- clipboard_selection_.reset();
+ return clipboard_owner_.Clear();
else
- primary_selection_.reset();
-}
-
-void Clipboard::AuraX11Details::HandleSelectionRequest(
- const XSelectionRequestEvent& event) {
- // Incrementally build our selection. By default this is a refusal, and we'll
- // override the parts indicating success in the different cases.
- XEvent reply;
- reply.xselection.type = SelectionNotify;
- reply.xselection.requestor = event.requestor;
- reply.xselection.selection = event.selection;
- reply.xselection.target = event.target;
- reply.xselection.property = None; // Indicates failure
- reply.xselection.time = event.time;
-
- // Get the proper selection.
- FormatMap* format_map = LookupStorageForAtom(event.selection);
- if (format_map) {
- ::Atom targets_atom = atom_cache_.GetAtom(kTargets);
- if (event.target == targets_atom) {
- std::vector< ::Atom> targets;
- targets.push_back(targets_atom);
- for (FormatMap::const_iterator it = format_map->begin();
- it != format_map->end(); ++it) {
- targets.push_back(it->first);
- }
-
- XChangeProperty(x_display_, event.requestor, event.property, XA_ATOM, 32,
- PropModeReplace,
- reinterpret_cast<unsigned char*>(&targets.front()),
- targets.size());
- reply.xselection.property = event.property;
- } else if (event.target == atom_cache_.GetAtom(kMultiple)) {
- // TODO(erg): Theoretically, the spec claims I'm supposed to handle the
- // MULTIPLE case, but I haven't seen it in the wild yet.
- NOTIMPLEMENTED();
- } else {
- // Try to find the data type in map.
- FormatMap::const_iterator it = format_map->find(event.target);
- if (it != format_map->end()) {
- XChangeProperty(x_display_, event.requestor, event.property,
- event.target, 8,
- PropModeReplace,
- reinterpret_cast<unsigned char*>(it->second.first),
- it->second.second);
- reply.xselection.property = event.property;
- }
- // I would put error logging here, but GTK ignores TARGETS and spams us
- // looking for its own internal types.
- }
- } else {
- DLOG(ERROR) << "Requested on a selection we don't support: "
- << XGetAtomName(x_display_, event.selection);
- }
-
- // Send off the reply.
- XSendEvent(x_display_, event.requestor, False, 0, &reply);
-}
-
-void Clipboard::AuraX11Details::HandleSelectionNotify(
- const XSelectionEvent& event) {
- if (!in_nested_loop_) {
- // This shouldn't happen; we're not waiting on the X server for data, but
- // any client can send any message...
- return;
- }
-
- if (current_selection_ == event.selection &&
- current_target_ == event.target) {
- returned_property_ = event.property;
- } else {
- // I am assuming that if some other client sent us a message after we've
- // asked for data, but it's malformed, we should just treat as if they sent
- // us an error message.
- returned_property_ = None;
- }
-
- quit_closure_.Run();
-}
-
-void Clipboard::AuraX11Details::HandleSelectionClear(
- const XSelectionClearEvent& event) {
- DLOG(ERROR) << "SelectionClear";
-
- // TODO(erg): If we receive a SelectionClear event while we're handling data,
- // we need to delay clearing.
-}
-
-void Clipboard::AuraX11Details::HandlePropertyNotify(
- const XPropertyEvent& event) {
- // TODO(erg): Must handle PropertyNotify events on our |x_window_| as part
- // of receiving data during paste.
+ return primary_owner_.Clear();
}
bool Clipboard::AuraX11Details::Dispatch(const base::NativeEvent& event) {
XEvent* xev = event;
switch (xev->type) {
- case SelectionRequest:
- HandleSelectionRequest(xev->xselectionrequest);
+ case SelectionRequest: {
+ if (xev->xselectionrequest.selection == XA_PRIMARY)
+ primary_owner_.OnSelectionRequest(xev->xselectionrequest);
+ else
+ clipboard_owner_.OnSelectionRequest(xev->xselectionrequest);
break;
- case SelectionNotify:
- HandleSelectionNotify(xev->xselection);
- break;
- case SelectionClear:
- HandleSelectionClear(xev->xselectionclear);
+ }
+ case SelectionNotify: {
+ if (xev->xselection.selection == XA_PRIMARY)
+ primary_requestor_.OnSelectionNotify(xev->xselection);
+ else
+ clipboard_requestor_.OnSelectionNotify(xev->xselection);
break;
- case PropertyNotify:
- HandlePropertyNotify(xev->xproperty);
+ }
+ case SelectionClear: {
+ if (xev->xselectionclear.selection == XA_PRIMARY)
+ primary_owner_.OnSelectionClear(xev->xselectionclear);
+ else
+ clipboard_owner_.OnSelectionClear(xev->xselectionclear);
break;
+ }
default:
break;
}
@@ -918,7 +659,8 @@ bool Clipboard::IsFormatAvailable(const FormatType& format,
DCHECK(CalledOnValidThread());
DCHECK(IsValidBuffer(buffer));
- TargetList target_list = aurax11_details_->WaitAndGetTargetsList(buffer);
+ TargetList target_list =
+ aurax11_details_->WaitAndGetTargetsList(buffer);
return target_list.ContainsFormat(format);
}
@@ -936,7 +678,8 @@ void Clipboard::ReadAvailableTypes(Buffer buffer, std::vector<string16>* types,
return;
}
- TargetList target_list = aurax11_details_->WaitAndGetTargetsList(buffer);
+ TargetList target_list =
+ aurax11_details_->WaitAndGetTargetsList(buffer);
types->clear();
diff --git a/ui/base/x/selection_owner.cc b/ui/base/x/selection_owner.cc
new file mode 100644
index 0000000..37e8663
--- /dev/null
+++ b/ui/base/x/selection_owner.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2013 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/x/selection_owner.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include "base/logging.h"
+#include "ui/base/x/selection_utils.h"
+
+namespace ui {
+
+namespace {
+
+const char kMultiple[] = "MULTIPLE";
+const char kTargets[] = "TARGETS";
+
+const char* kAtomsToCache[] = {
+ kMultiple,
+ kTargets,
+ NULL
+};
+
+} // namespace
+
+SelectionOwner::SelectionOwner(Display* x_display,
+ Window x_window,
+ Atom selection_name)
+ : x_display_(x_display),
+ x_window_(x_window),
+ selection_name_(selection_name),
+ atom_cache_(x_display_, kAtomsToCache) {
+}
+
+SelectionOwner::~SelectionOwner() {
+}
+
+void SelectionOwner::TakeOwnershipOfSelection(
+ scoped_ptr<SelectionFormatMap> data) {
+ XSetSelectionOwner(x_display_, selection_name_, x_window_, CurrentTime);
+
+ if (XGetSelectionOwner(x_display_, selection_name_) == x_window_) {
+ // The X server agrees that we are the selection owner. Commit our data.
+ selection_data_ = data.Pass();
+ }
+}
+
+void SelectionOwner::Clear() {
+ if (XGetSelectionOwner(x_display_, selection_name_) == x_window_)
+ XSetSelectionOwner(x_display_, selection_name_, None, CurrentTime);
+
+ selection_data_.reset();
+}
+
+void SelectionOwner::OnSelectionRequest(const XSelectionRequestEvent& event) {
+ // Incrementally build our selection. By default this is a refusal, and we'll
+ // override the parts indicating success in the different cases.
+ XEvent reply;
+ reply.xselection.type = SelectionNotify;
+ reply.xselection.requestor = event.requestor;
+ reply.xselection.selection = event.selection;
+ reply.xselection.target = event.target;
+ reply.xselection.property = None; // Indicates failure
+ reply.xselection.time = event.time;
+
+ // Get the proper selection.
+ if (selection_data_.get()) {
+ Atom targets_atom = atom_cache_.GetAtom(kTargets);
+ if (event.target == targets_atom) {
+ // We have been asked for TARGETS. Send an atom array back with the data
+ // types we support.
+ std::vector<Atom> targets;
+ targets.push_back(targets_atom);
+ for (SelectionFormatMap::const_iterator it = selection_data_->begin();
+ it != selection_data_->end(); ++it) {
+ targets.push_back(it->first);
+ }
+
+ XChangeProperty(x_display_, event.requestor, event.property, XA_ATOM, 32,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(&targets.front()),
+ targets.size());
+ reply.xselection.property = event.property;
+ } else if (event.target == atom_cache_.GetAtom(kMultiple)) {
+ // TODO(erg): Theoretically, the spec claims I'm supposed to handle the
+ // MULTIPLE case, but I haven't seen it in the wild yet.
+ NOTIMPLEMENTED();
+ } else {
+ // Try to find the data type in map.
+ SelectionFormatMap::const_iterator it =
+ selection_data_->find(event.target);
+ if (it != selection_data_->end()) {
+ XChangeProperty(x_display_, event.requestor, event.property,
+ event.target, 8,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(it->second.first),
+ it->second.second);
+ reply.xselection.property = event.property;
+ }
+ // I would put error logging here, but GTK ignores TARGETS and spams us
+ // looking for its own internal types.
+ }
+ } else {
+ DLOG(ERROR) << "XWindow " << x_window_ << " received a SelectionRequest "
+ << "message when we don't have data to offer.";
+ }
+
+ // Send off the reply.
+ XSendEvent(x_display_, event.requestor, False, 0, &reply);
+}
+
+void SelectionOwner::OnSelectionClear(const XSelectionClearEvent& event) {
+ DLOG(ERROR) << "SelectionClear";
+
+ // TODO(erg): If we receive a SelectionClear event while we're handling data,
+ // we need to delay clearing.
+}
+
+} // namespace ui
+
diff --git a/ui/base/x/selection_owner.h b/ui/base/x/selection_owner.h
new file mode 100644
index 0000000..eb10653
--- /dev/null
+++ b/ui/base/x/selection_owner.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2013 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.
+
+#ifndef UI_BASE_X_SELECTION_OWNER_H_
+#define UI_BASE_X_SELECTION_OWNER_H_
+
+#include <X11/Xlib.h>
+
+// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class.
+#undef RootWindow
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "ui/base/ui_export.h"
+#include "ui/base/x/x11_atom_cache.h"
+
+namespace ui {
+
+class SelectionFormatMap;
+
+// Owns a specific X11 selection on an X window.
+//
+// The selection owner object keeps track of which xwindow is the current
+// owner, and when its |xwindow_|, offers different data types to other
+// processes.
+class UI_EXPORT SelectionOwner {
+ public:
+ SelectionOwner(Display* xdisplay,
+ ::Window xwindow,
+ ::Atom selection_name);
+ ~SelectionOwner();
+
+ // Returns the current selection data. Useful for fast paths.
+ SelectionFormatMap* selection_format_map() { return selection_data_.get(); }
+
+ // Attempts to take ownership of the selection. If we're successful, present
+ // |data| to other windows.
+ void TakeOwnershipOfSelection(scoped_ptr<SelectionFormatMap> data);
+
+ // Releases the selection (if we own it) and clears any data we own.
+ void Clear();
+
+ // It is our owner's responsibility to plumb X11 events on |xwindow_| to us.
+ void OnSelectionRequest(const XSelectionRequestEvent& event);
+ void OnSelectionClear(const XSelectionClearEvent& event);
+ // TODO(erg): Do we also need to follow PropertyNotify events? We currently
+ // don't, but there were open todos in the previous implementation.
+
+ private:
+ // Our X11 state.
+ Display* x_display_;
+ ::Window x_window_;
+
+ // The X11 selection that this instance communicates on.
+ ::Atom selection_name_;
+
+ // The data we are currently serving.
+ scoped_ptr<SelectionFormatMap> selection_data_;
+
+ X11AtomCache atom_cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(SelectionOwner);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_X_SELECTION_OWNER_H_
diff --git a/ui/base/x/selection_requestor.cc b/ui/base/x/selection_requestor.cc
new file mode 100644
index 0000000..0aec086
--- /dev/null
+++ b/ui/base/x/selection_requestor.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2013 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/x/selection_requestor.h"
+
+#include "base/message_pump_aurax11.h"
+#include "base/run_loop.h"
+
+namespace ui {
+
+namespace {
+
+const char kChromeSelection[] = "CHROME_SELECTION";
+
+const char* kAtomsToCache[] = {
+ kChromeSelection,
+ NULL
+};
+
+} // namespace
+
+SelectionRequestor::SelectionRequestor(Display* x_display,
+ Window x_window,
+ Atom selection_name)
+ : x_display_(x_display),
+ x_window_(x_window),
+ in_nested_loop_(false),
+ selection_name_(selection_name),
+ current_target_(None),
+ returned_property_(None),
+ atom_cache_(x_display_, kAtomsToCache) {
+}
+
+SelectionRequestor::~SelectionRequestor() {}
+
+bool SelectionRequestor::PerformBlockingConvertSelection(
+ Atom target,
+ unsigned char** out_data,
+ size_t* out_data_bytes,
+ size_t* out_data_items,
+ Atom* out_type) {
+ // The name of the property we're asking to be set on |x_window_|.
+ Atom property_to_set = atom_cache_.GetAtom(kChromeSelection);
+
+ XConvertSelection(x_display_,
+ selection_name_,
+ target,
+ property_to_set,
+ x_window_,
+ CurrentTime);
+
+ // Now that we've thrown our message off to the X11 server, we block waiting
+ // for a response.
+ MessageLoopForUI* loop = MessageLoopForUI::current();
+ MessageLoop::ScopedNestableTaskAllower allow_nested(loop);
+ base::RunLoop run_loop(base::MessagePumpAuraX11::Current());
+
+ current_target_ = target;
+ in_nested_loop_ = true;
+ quit_closure_ = run_loop.QuitClosure();
+ run_loop.Run();
+ in_nested_loop_ = false;
+ current_target_ = None;
+
+ if (returned_property_ != property_to_set)
+ return false;
+
+ // Retrieve the data from our window.
+ unsigned long nitems = 0;
+ unsigned long nbytes = 0;
+ Atom prop_type = None;
+ int prop_format = 0;
+ unsigned char* property_data = NULL;
+ if (XGetWindowProperty(x_display_,
+ x_window_,
+ returned_property_,
+ 0, 0x1FFFFFFF /* MAXINT32 / 4 */, False,
+ AnyPropertyType, &prop_type, &prop_format,
+ &nitems, &nbytes, &property_data) != Success) {
+ return false;
+ }
+
+ if (prop_type == None)
+ return false;
+
+ if (out_data)
+ *out_data = property_data;
+
+ if (out_data_bytes) {
+ // So even though we should theoretically have nbytes (and we can't
+ // pass NULL there), we need to manually calculate the byte length here
+ // because nbytes always returns zero.
+ switch (prop_format) {
+ case 8:
+ *out_data_bytes = nitems;
+ break;
+ case 16:
+ *out_data_bytes = sizeof(short) * nitems;
+ break;
+ case 32:
+ *out_data_bytes = sizeof(long) * nitems;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ if (out_data_items)
+ *out_data_items = nitems;
+
+ if (out_type)
+ *out_type = prop_type;
+
+ return true;
+}
+
+void SelectionRequestor::OnSelectionNotify(const XSelectionEvent& event) {
+ if (!in_nested_loop_) {
+ // This shouldn't happen; we're not waiting on the X server for data, but
+ // any client can send any message...
+ return;
+ }
+
+ if (selection_name_ == event.selection &&
+ current_target_ == event.target) {
+ returned_property_ = event.property;
+ } else {
+ // I am assuming that if some other client sent us a message after we've
+ // asked for data, but it's malformed, we should just treat as if they sent
+ // us an error message.
+ returned_property_ = None;
+ }
+
+ quit_closure_.Run();
+}
+
+} // namespace ui
diff --git a/ui/base/x/selection_requestor.h b/ui/base/x/selection_requestor.h
new file mode 100644
index 0000000..10a7c06
--- /dev/null
+++ b/ui/base/x/selection_requestor.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2013 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.
+
+#ifndef UI_BASE_X_SELECTION_REQUESTOR_H_
+#define UI_BASE_X_SELECTION_REQUESTOR_H_
+
+#include <X11/Xlib.h>
+
+// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class.
+#undef RootWindow
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "ui/base/ui_export.h"
+#include "ui/base/x/x11_atom_cache.h"
+
+namespace ui {
+
+// Requests and later receives data from the X11 server through the selection
+// system.
+//
+// X11 uses a system called "selections" to implement clipboards and drag and
+// drop. This class interprets messages from the statefull selection request
+// API. SelectionRequestor should only deal with the X11 details; it does not
+// implement per-component fast-paths.
+class UI_EXPORT SelectionRequestor {
+ public:
+ SelectionRequestor(Display* xdisplay,
+ ::Window xwindow,
+ ::Atom selection_name);
+ ~SelectionRequestor();
+
+ // Does the work of requesting |target| from the selection we handle,
+ // spinning up the nested message loop, and reading the resulting data
+ // back. |out_data| is allocated with the X allocator and must be freed with
+ // XFree(). |out_data_bytes| is the length in machine chars, while
+ // |out_data_items| is the length in |out_type| items.
+ bool PerformBlockingConvertSelection(::Atom target,
+ unsigned char** out_data,
+ size_t* out_data_bytes,
+ size_t* out_data_items,
+ ::Atom* out_type);
+
+ // It is our owner's responsibility to plumb X11 SelectionNotify events on
+ // |xwindow_| to us.
+ void OnSelectionNotify(const XSelectionEvent& event);
+
+ private:
+ // Our X11 state.
+ Display* x_display_;
+ ::Window x_window_;
+
+ // True if we're currently running a nested message loop, waiting for data to
+ // come back from the X server.
+ bool in_nested_loop_;
+
+ // The X11 selection that this instance communicates on.
+ ::Atom selection_name_;
+
+ // Data to the current XConvertSelection request. Used for error detection;
+ // we verify it on the return message.
+ ::Atom current_target_;
+
+ // The property in the returning SelectNotify message is used to signal
+ // success. If None, our request failed somehow. If equal to the property
+ // atom that we sent in the XConvertSelection call, we can read that property
+ // on |x_window_| for the requested data.
+ ::Atom returned_property_;
+
+ // Called to terminate the nested message loop.
+ base::Closure quit_closure_;
+
+ X11AtomCache atom_cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(SelectionRequestor);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_X_SELECTION_REQUESTOR_H_
diff --git a/ui/base/x/selection_utils.cc b/ui/base/x/selection_utils.cc
new file mode 100644
index 0000000..653a5f2
--- /dev/null
+++ b/ui/base/x/selection_utils.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2013 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/x/selection_utils.h"
+
+#include <set>
+
+#include "base/logging.h"
+#include "ui/base/x/x11_atom_cache.h"
+
+namespace ui {
+
+std::vector< ::Atom> GetTextAtomsFrom(const X11AtomCache* atom_cache) {
+ std::vector< ::Atom> atoms;
+ atoms.push_back(atom_cache->GetAtom("UTF8_STRING"));
+ atoms.push_back(atom_cache->GetAtom("STRING"));
+ atoms.push_back(atom_cache->GetAtom("TEXT"));
+ return atoms;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SelectionFormatMap::SelectionFormatMap() {}
+
+SelectionFormatMap::~SelectionFormatMap() {
+ // WriteText() inserts the same pointer multiple times for different
+ // representations; we need to dedupe it.
+ std::set<char*> to_delete;
+ for (InternalMap::iterator it = data_.begin(); it != data_.end(); ++it)
+ to_delete.insert(it->second.first);
+
+ for (std::set<char*>::iterator it = to_delete.begin(); it != to_delete.end();
+ ++it) {
+ delete [] *it;
+ }
+}
+
+void SelectionFormatMap::Insert(::Atom atom, char* data, size_t size) {
+ DCHECK(data_.find(atom) == data_.end());
+ data_.insert(std::make_pair(atom, std::make_pair(data, size)));
+}
+
+} // namespace ui
diff --git a/ui/base/x/selection_utils.h b/ui/base/x/selection_utils.h
new file mode 100644
index 0000000..eba259b
--- /dev/null
+++ b/ui/base/x/selection_utils.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2013 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.
+
+#ifndef UI_BASE_X_SELECTION_UTILS_H_
+#define UI_BASE_X_SELECTION_UTILS_H_
+
+#include <X11/Xlib.h>
+
+// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class.
+#undef RootWindow
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/ui_export.h"
+
+namespace ui {
+class X11AtomCache;
+
+// Returns a list of all text atoms that we handle.
+UI_EXPORT std::vector< ::Atom> GetTextAtomsFrom(const X11AtomCache* atom_cache);
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Represents the selection in different data formats. Binary data passed in is
+// assumed to be allocated with new char[], and is owned by SelectionFormatMap.
+class UI_EXPORT SelectionFormatMap {
+ public:
+ // Our internal data store, which we only expose through iterators.
+ typedef std::map< ::Atom, std::pair<char*, size_t> > InternalMap;
+ typedef std::map< ::Atom, std::pair<char*, size_t> >::const_iterator
+ const_iterator;
+
+ SelectionFormatMap();
+ ~SelectionFormatMap();
+
+ // Adds the selection in the format |atom|. Ownership of |data| is passed to
+ // us.
+ void Insert(::Atom atom, char* data, size_t size);
+
+ // Pass through to STL map. Only allow non-mutation access.
+ const_iterator begin() const { return data_.begin(); }
+ const_iterator end() const { return data_.end(); }
+ const_iterator find(::Atom atom) const { return data_.find(atom); }
+ size_t size() const { return data_.size(); }
+
+ private:
+ InternalMap data_;
+
+ DISALLOW_COPY_AND_ASSIGN(SelectionFormatMap);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_X_SELECTION_UTILS_H_
diff --git a/ui/ui.gyp b/ui/ui.gyp
index 5ad2a7f..540b547 100644
--- a/ui/ui.gyp
+++ b/ui/ui.gyp
@@ -331,6 +331,12 @@
'base/x/events_x.cc',
'base/x/root_window_property_watcher_x.cc',
'base/x/root_window_property_watcher_x.h',
+ 'base/x/selection_owner.cc',
+ 'base/x/selection_owner.h',
+ 'base/x/selection_requestor.cc',
+ 'base/x/selection_requestor.h',
+ 'base/x/selection_utils.cc',
+ 'base/x/selection_utils.h',
'base/x/valuators.cc',
'base/x/valuators.h',
'base/x/work_area_watcher_x.cc',
@@ -607,6 +613,12 @@
'base/cursor/cursor_loader_x11.h',
'base/cursor/cursor_win.cc',
'base/cursor/cursor_x11.cc',
+ 'base/x/selection_owner.cc',
+ 'base/x/selection_owner.h',
+ 'base/x/selection_requestor.cc',
+ 'base/x/selection_requestor.h',
+ 'base/x/selection_utils.cc',
+ 'base/x/selection_utils.h',
]
}],
['use_aura==1 and OS=="win"', {