summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorerg@chromium.org <erg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-19 13:09:56 +0000
committererg@chromium.org <erg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-19 13:09:56 +0000
commit022731a384c7666aff36f8d87ec627d7bedf8995 (patch)
tree00552ba63ce3799e333cc176c78a0d30110bf4a0
parent85f50ec0ad933ff17c19ef33e70b38141e0bcb1a (diff)
downloadchromium_src-022731a384c7666aff36f8d87ec627d7bedf8995.zip
chromium_src-022731a384c7666aff36f8d87ec627d7bedf8995.tar.gz
chromium_src-022731a384c7666aff36f8d87ec627d7bedf8995.tar.bz2
linux_aura: Make chrome windows drag sources on X11.
This implements the source side of the Xdnd protocol. It also includes some small changes to which object owns the drag selection so that we can do that part entirely asynchronously without spinning up a nested message loop. This patch only handles text drags and doesn't change the cursor or show a drag image under the cursor. Both of these require some refactoring and this patch is big enough as is. BUG=130806 Review URL: https://chromiumcodereview.appspot.com/16271006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@207219 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--ui/base/clipboard/clipboard_aurax11.cc2
-rw-r--r--ui/base/dragdrop/desktop_selection_provider_aurax11.h24
-rw-r--r--ui/base/dragdrop/os_exchange_data_provider_aurax11.cc100
-rw-r--r--ui/base/dragdrop/os_exchange_data_provider_aurax11.h41
-rw-r--r--ui/base/x/selection_owner.cc14
-rw-r--r--ui/base/x/selection_owner.h3
-rw-r--r--ui/base/x/selection_requestor.cc54
-rw-r--r--ui/base/x/selection_utils.cc57
-rw-r--r--ui/base/x/selection_utils.h13
-rw-r--r--ui/base/x/x11_util.cc103
-rw-r--r--ui/base/x/x11_util.h16
-rw-r--r--ui/ui.gyp1
-rw-r--r--ui/views/controls/textfield/native_textfield_views_unittest.cc36
-rw-r--r--ui/views/views.gyp1
-rw-r--r--ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc649
-rw-r--r--ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h99
-rw-r--r--ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc20
-rw-r--r--ui/views/widget/desktop_aura/desktop_root_window_host_x11.h9
-rw-r--r--ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc4
-rw-r--r--ui/views/widget/desktop_aura/x11_desktop_window_move_client.h1
-rw-r--r--ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc3
-rw-r--r--ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h3
22 files changed, 960 insertions, 293 deletions
diff --git a/ui/base/clipboard/clipboard_aurax11.cc b/ui/base/clipboard/clipboard_aurax11.cc
index 45435a7..8809d26 100644
--- a/ui/base/clipboard/clipboard_aurax11.cc
+++ b/ui/base/clipboard/clipboard_aurax11.cc
@@ -399,7 +399,7 @@ scoped_ptr<SelectionData> Clipboard::AuraX11Details::RequestAndWaitForTypes(
it != types.end(); ++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(x_display_));
+ scoped_ptr<SelectionData> data_out(new SelectionData);
data_out->Set(format_map_it->first, format_map_it->second.first,
format_map_it->second.second, false);
return data_out.Pass();
diff --git a/ui/base/dragdrop/desktop_selection_provider_aurax11.h b/ui/base/dragdrop/desktop_selection_provider_aurax11.h
deleted file mode 100644
index 383245f..0000000
--- a/ui/base/dragdrop/desktop_selection_provider_aurax11.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// 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.
-
-#ifndef UI_BASE_DRAGDROP_DESKTOP_SELECTION_PROVIDER_AURAX11_H_
-#define UI_BASE_DRAGDROP_DESKTOP_SELECTION_PROVIDER_AURAX11_H_
-
-#include "ui/base/ui_export.h"
-
-namespace ui {
-class OSExchangeDataProviderAuraX11;
-
-// Used by OSExchangeDataProviderAuraX11 to attach itself to an object which
-// can provide it with SelectionNotify events.
-class UI_EXPORT DesktopSelectionProviderAuraX11 {
- public:
- virtual ~DesktopSelectionProviderAuraX11() {}
-
- virtual void SetDropHandler(OSExchangeDataProviderAuraX11* handler) = 0;
-};
-
-} // namespace
-
-#endif // UI_BASE_DRAGDROP_DESKTOP_SELECTION_PROVIDER_AURAX11_H_
diff --git a/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc b/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc
index 040ac8f..d760a72 100644
--- a/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc
+++ b/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc
@@ -11,7 +11,6 @@
#include "net/base/net_util.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
-#include "ui/base/dragdrop/desktop_selection_provider_aurax11.h"
#include "ui/base/x/selection_utils.h"
#include "ui/base/x/x11_util.h"
@@ -33,37 +32,31 @@ const char* kAtomsToCache[] = {
kDndSelection,
Clipboard::kMimeTypeURIList,
kMimeTypeMozillaURL,
+ Clipboard::kMimeTypeText,
NULL
};
} // namespace
OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11(
- ui::DesktopSelectionProviderAuraX11* provider,
::Window x_window,
- const std::vector< ::Atom> targets)
+ scoped_ptr<SelectionFormatMap> selection)
: x_display_(GetXDisplay()),
x_root_window_(DefaultRootWindow(x_display_)),
own_window_(false),
- selection_event_provider_(provider),
x_window_(x_window),
atom_cache_(x_display_, kAtomsToCache),
- selection_requestor_(x_display_, x_window_,
- atom_cache_.GetAtom(kDndSelection)),
+ format_map_(selection.Pass()),
selection_owner_(x_display_, x_window_,
- atom_cache_.GetAtom(kDndSelection)),
- targets_(targets) {
+ atom_cache_.GetAtom(kDndSelection)) {
// We don't know all possible MIME types at compile time.
atom_cache_.allow_uncached_atoms();
-
- selection_event_provider_->SetDropHandler(this);
}
OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11()
: x_display_(GetXDisplay()),
x_root_window_(DefaultRootWindow(x_display_)),
own_window_(true),
- selection_event_provider_(NULL),
x_window_(XCreateWindow(
x_display_,
x_root_window_,
@@ -75,8 +68,7 @@ OSExchangeDataProviderAuraX11::OSExchangeDataProviderAuraX11()
0,
NULL)),
atom_cache_(x_display_, kAtomsToCache),
- selection_requestor_(x_display_, x_window_,
- atom_cache_.GetAtom(kDndSelection)),
+ format_map_(new SelectionFormatMap),
selection_owner_(x_display_, x_window_,
atom_cache_.GetAtom(kDndSelection)) {
// We don't know all possible MIME types at compile time.
@@ -91,18 +83,42 @@ OSExchangeDataProviderAuraX11::~OSExchangeDataProviderAuraX11() {
if (own_window_) {
base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(x_window_);
XDestroyWindow(x_display_, x_window_);
- } else {
- selection_event_provider_->SetDropHandler(NULL);
}
}
-void OSExchangeDataProviderAuraX11::OnSelectionNotify(
- const XSelectionEvent& event) {
- selection_requestor_.OnSelectionNotify(event);
+void OSExchangeDataProviderAuraX11::TakeOwnershipOfSelection() const {
+ selection_owner_.TakeOwnershipOfSelection(
+ scoped_ptr<SelectionFormatMap>(format_map_->Clone()));
}
-void OSExchangeDataProviderAuraX11::SetString(const string16& data) {
- NOTIMPLEMENTED();
+void OSExchangeDataProviderAuraX11::RetrieveTargets(
+ std::vector<Atom>* targets) const {
+ selection_owner_.RetrieveTargets(targets);
+}
+
+scoped_ptr<SelectionFormatMap>
+OSExchangeDataProviderAuraX11::CloneFormatMap() const {
+ // We clone the |selection_owner_|'s format map instead of our own in case
+ // ours has been modified since TakeOwnershipOfSelection() was called.
+ return selection_owner_.selection_format_map()->Clone();
+}
+
+void OSExchangeDataProviderAuraX11::SetString(const string16& text_data) {
+ std::string utf8 = UTF16ToUTF8(text_data);
+
+ // Ownership of |data| is passed to |format_map_|.
+ size_t text_len = utf8.size();
+ char* data = new char[text_len];
+ memcpy(data, utf8.c_str(), text_len);
+
+ format_map_->Insert(
+ atom_cache_.GetAtom(Clipboard::kMimeTypeText), data, text_len);
+ format_map_->Insert(
+ atom_cache_.GetAtom(kText), data, text_len);
+ format_map_->Insert(
+ atom_cache_.GetAtom(kString), data, text_len);
+ format_map_->Insert(
+ atom_cache_.GetAtom(kUtf8String), data, text_len);
}
void OSExchangeDataProviderAuraX11::SetURL(const GURL& url,
@@ -128,10 +144,9 @@ void OSExchangeDataProviderAuraX11::SetPickledData(
bool OSExchangeDataProviderAuraX11::GetString(string16* result) const {
std::vector< ::Atom> text_atoms = ui::GetTextAtomsFrom(&atom_cache_);
std::vector< ::Atom> requested_types;
- ui::GetAtomIntersection(text_atoms, targets_, &requested_types);
+ ui::GetAtomIntersection(text_atoms, GetTargets(), &requested_types);
- scoped_ptr<ui::SelectionData> data(
- selection_requestor_.RequestAndWaitForTypes(requested_types));
+ scoped_ptr<ui::SelectionData> data(format_map_->GetFirstOf(requested_types));
if (data) {
std::string text = data->GetText();
*result = UTF8ToUTF16(text);
@@ -145,10 +160,9 @@ bool OSExchangeDataProviderAuraX11::GetURLAndTitle(GURL* url,
string16* title) const {
std::vector< ::Atom> url_atoms = ui::GetURLAtomsFrom(&atom_cache_);
std::vector< ::Atom> requested_types;
- ui::GetAtomIntersection(url_atoms, targets_, &requested_types);
+ ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
- scoped_ptr<ui::SelectionData> data(
- selection_requestor_.RequestAndWaitForTypes(requested_types));
+ scoped_ptr<ui::SelectionData> data(format_map_->GetFirstOf(requested_types));
if (data) {
// TODO(erg): Technically, both of these forms can accept multiple URLs,
// but that doesn't match the assumptions of the rest of the system which
@@ -213,14 +227,14 @@ bool OSExchangeDataProviderAuraX11::GetPickledData(
bool OSExchangeDataProviderAuraX11::HasString() const {
std::vector< ::Atom> text_atoms = ui::GetTextAtomsFrom(&atom_cache_);
std::vector< ::Atom> requested_types;
- ui::GetAtomIntersection(text_atoms, targets_, &requested_types);
+ ui::GetAtomIntersection(text_atoms, GetTargets(), &requested_types);
return !requested_types.empty();
}
bool OSExchangeDataProviderAuraX11::HasURL() const {
std::vector< ::Atom> url_atoms = ui::GetURLAtomsFrom(&atom_cache_);
std::vector< ::Atom> requested_types;
- ui::GetAtomIntersection(url_atoms, targets_, &requested_types);
+ ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
return !requested_types.empty();
}
@@ -234,7 +248,7 @@ bool OSExchangeDataProviderAuraX11::HasCustomFormat(
std::vector< ::Atom> url_atoms;
url_atoms.push_back(atom_cache_.GetAtom(format.ToString().c_str()));
std::vector< ::Atom> requested_types;
- ui::GetAtomIntersection(url_atoms, targets_, &requested_types);
+ ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
return !requested_types.empty();
}
@@ -249,10 +263,9 @@ bool OSExchangeDataProviderAuraX11::GetHtml(string16* html,
std::vector< ::Atom> url_atoms;
url_atoms.push_back(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML));
std::vector< ::Atom> requested_types;
- ui::GetAtomIntersection(url_atoms, targets_, &requested_types);
+ ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
- scoped_ptr<ui::SelectionData> data(
- selection_requestor_.RequestAndWaitForTypes(requested_types));
+ scoped_ptr<ui::SelectionData> data(format_map_->GetFirstOf(requested_types));
if (data) {
*html = data->GetHtml();
*base_url = GURL();
@@ -266,7 +279,7 @@ bool OSExchangeDataProviderAuraX11::HasHtml() const {
std::vector< ::Atom> url_atoms;
url_atoms.push_back(atom_cache_.GetAtom(Clipboard::kMimeTypeHTML));
std::vector< ::Atom> requested_types;
- ui::GetAtomIntersection(url_atoms, targets_, &requested_types);
+ ui::GetAtomIntersection(url_atoms, GetTargets(), &requested_types);
return !requested_types.empty();
}
@@ -274,22 +287,29 @@ bool OSExchangeDataProviderAuraX11::HasHtml() const {
void OSExchangeDataProviderAuraX11::SetDragImage(
const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset) {
- NOTIMPLEMENTED();
+ drag_image_ = image;
+ drag_image_offset_ = cursor_offset;
}
const gfx::ImageSkia& OSExchangeDataProviderAuraX11::GetDragImage() const {
- NOTIMPLEMENTED();
return drag_image_;
}
const gfx::Vector2d& OSExchangeDataProviderAuraX11::GetDragImageOffset() const {
- NOTIMPLEMENTED();
return drag_image_offset_;
}
bool OSExchangeDataProviderAuraX11::Dispatch(const base::NativeEvent& event) {
- // TODO(erg): Implement this side when we implement sending data.
- return false;
+ XEvent* xev = event;
+ switch (xev->type) {
+ case SelectionRequest:
+ selection_owner_.OnSelectionRequest(xev->xselectionrequest);
+ break;
+ default:
+ NOTIMPLEMENTED();
+ }
+
+ return true;
}
bool OSExchangeDataProviderAuraX11::GetPlainTextURL(GURL* url) const {
@@ -297,6 +317,10 @@ bool OSExchangeDataProviderAuraX11::GetPlainTextURL(GURL* url) const {
return false;
}
+std::vector< ::Atom> OSExchangeDataProviderAuraX11::GetTargets() const {
+ return format_map_->GetTypes();
+}
+
///////////////////////////////////////////////////////////////////////////////
// OSExchangeData, public:
diff --git a/ui/base/dragdrop/os_exchange_data_provider_aurax11.h b/ui/base/dragdrop/os_exchange_data_provider_aurax11.h
index c2ae861..ea41131 100644
--- a/ui/base/dragdrop/os_exchange_data_provider_aurax11.h
+++ b/ui/base/dragdrop/os_exchange_data_provider_aurax11.h
@@ -26,19 +26,16 @@
namespace ui {
class Clipboard;
-class DesktopSelectionProviderAuraX11;
// OSExchangeData::Provider implementation for aura on linux.
class UI_EXPORT OSExchangeDataProviderAuraX11
: public OSExchangeData::Provider,
public base::MessagePumpDispatcher {
public:
- // Creates a Provider for drag receiving. |x_window| is the window the cursor
- // is over, and |targets| are the MIME types being offered by the other
- // process.
- OSExchangeDataProviderAuraX11(ui::DesktopSelectionProviderAuraX11* provider,
- ::Window x_window,
- const std::vector< ::Atom> targets);
+ // |x_window| is the window the cursor is over, and |selection| is the set of
+ // data being offered.
+ OSExchangeDataProviderAuraX11(::Window x_window,
+ scoped_ptr<SelectionFormatMap> selection);
// Creates a Provider for sending drag information. This creates its own,
// hidden X11 window to own send data.
@@ -46,9 +43,16 @@ class UI_EXPORT OSExchangeDataProviderAuraX11
virtual ~OSExchangeDataProviderAuraX11();
- // If we are receiving, we receive messages from a DesktopRootWindowHost
- // through this interface.
- void OnSelectionNotify(const XSelectionEvent& event);
+ // After all the Set* methods have built up the data we're offering, call
+ // this to take ownership of the XdndSelection clipboard.
+ void TakeOwnershipOfSelection() const;
+
+ // Retrieves a list of types we're offering. Noop if we haven't taken the
+ // selection.
+ void RetrieveTargets(std::vector<Atom>* targets) const;
+
+ // Makes a copy of the format map currently being offered.
+ scoped_ptr<SelectionFormatMap> CloneFormatMap() const;
// Overridden from OSExchangeData::Provider:
virtual void SetString(const string16& data) OVERRIDE;
@@ -89,6 +93,9 @@ class UI_EXPORT OSExchangeDataProviderAuraX11
// parsed as a URL.
bool GetPlainTextURL(GURL* url) const;
+ // Returns the targets in |format_map_|.
+ std::vector< ::Atom> GetTargets() const;
+
// Drag image and offset data.
gfx::ImageSkia drag_image_;
gfx::Vector2d drag_image_offset_;
@@ -105,19 +112,17 @@ class UI_EXPORT OSExchangeDataProviderAuraX11
// our own xwindow just to receive events on it.
const bool own_window_;
- // When we don't own the window, we keep track of the object that does so we
- // can receive messages from it.
- ui::DesktopSelectionProviderAuraX11* selection_event_provider_;
-
::Window x_window_;
X11AtomCache atom_cache_;
- mutable SelectionRequestor selection_requestor_;
- mutable SelectionOwner selection_owner_;
+ // A representation of data. This is either passed to us from the other
+ // process, or built up through a sequence of Set*() calls. It can be passed
+ // to |selection_owner_| when we take the selection.
+ scoped_ptr<SelectionFormatMap> format_map_;
- // The mime types we have been offered by the source window.
- std::vector< ::Atom> targets_;
+ // Takes a snapshot of |format_map_| and offers it to other windows.
+ mutable SelectionOwner selection_owner_;
DISALLOW_COPY_AND_ASSIGN(OSExchangeDataProviderAuraX11);
};
diff --git a/ui/base/x/selection_owner.cc b/ui/base/x/selection_owner.cc
index 37e8663..a5b0f4e 100644
--- a/ui/base/x/selection_owner.cc
+++ b/ui/base/x/selection_owner.cc
@@ -35,6 +35,15 @@ SelectionOwner::SelectionOwner(Display* x_display,
}
SelectionOwner::~SelectionOwner() {
+ Clear();
+}
+
+void SelectionOwner::RetrieveTargets(std::vector<Atom>* targets) {
+ targets->clear();
+ for (SelectionFormatMap::const_iterator it = selection_data_->begin();
+ it != selection_data_->end(); ++it) {
+ targets->push_back(it->first);
+ }
}
void SelectionOwner::TakeOwnershipOfSelection(
@@ -73,10 +82,7 @@ void SelectionOwner::OnSelectionRequest(const XSelectionRequestEvent& event) {
// 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);
- }
+ RetrieveTargets(&targets);
XChangeProperty(x_display_, event.requestor, event.property, XA_ATOM, 32,
PropModeReplace,
diff --git a/ui/base/x/selection_owner.h b/ui/base/x/selection_owner.h
index eb10653..a62be52 100644
--- a/ui/base/x/selection_owner.h
+++ b/ui/base/x/selection_owner.h
@@ -36,6 +36,9 @@ class UI_EXPORT SelectionOwner {
// Returns the current selection data. Useful for fast paths.
SelectionFormatMap* selection_format_map() { return selection_data_.get(); }
+ // Retrieves a list of types we're offering.
+ void RetrieveTargets(std::vector<Atom>* targets);
+
// Attempts to take ownership of the selection. If we're successful, present
// |data| to other windows.
void TakeOwnershipOfSelection(scoped_ptr<SelectionFormatMap> data);
diff --git a/ui/base/x/selection_requestor.cc b/ui/base/x/selection_requestor.cc
index bd89271..1fd2872 100644
--- a/ui/base/x/selection_requestor.cc
+++ b/ui/base/x/selection_requestor.cc
@@ -7,6 +7,7 @@
#include "base/message_loop/message_pump_aurax11.h"
#include "base/run_loop.h"
#include "ui/base/x/selection_utils.h"
+#include "ui/base/x/x11_util.h"
namespace ui {
@@ -67,54 +68,9 @@ bool SelectionRequestor::PerformBlockingConvertSelection(
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;
+ return ui::GetRawBytesOfProperty(x_window_, returned_property_,
+ out_data, out_data_bytes, out_data_items,
+ out_type);
}
scoped_ptr<SelectionData> SelectionRequestor::RequestAndWaitForTypes(
@@ -130,7 +86,7 @@ scoped_ptr<SelectionData> SelectionRequestor::RequestAndWaitForTypes(
NULL,
&type) &&
type == *it) {
- scoped_ptr<SelectionData> data_out(new SelectionData(x_display_));
+ scoped_ptr<SelectionData> data_out(new SelectionData);
data_out->Set(type, (char*)data, data_bytes, true);
return data_out.Pass();
}
diff --git a/ui/base/x/selection_utils.cc b/ui/base/x/selection_utils.cc
index 307ce87..5dd6f68 100644
--- a/ui/base/x/selection_utils.cc
+++ b/ui/base/x/selection_utils.cc
@@ -11,6 +11,7 @@
#include "base/strings/utf_string_conversions.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/x/x11_atom_cache.h"
+#include "ui/base/x/x11_util.h"
namespace ui {
@@ -75,18 +76,68 @@ SelectionFormatMap::~SelectionFormatMap() {
}
void SelectionFormatMap::Insert(::Atom atom, char* data, size_t size) {
- DCHECK(data_.find(atom) == data_.end());
+ // Views code often inserts the same content multiple times, so we have to
+ // free old data. Only call delete when it's the last pointer we have to that
+ // data.
+ InternalMap::iterator exists_it = data_.find(atom);
+ if (exists_it != data_.end()) {
+ int count = 0;
+ for (InternalMap::iterator it = data_.begin(); it != data_.end(); ++it) {
+ if (it->second.first == exists_it->second.first)
+ count++;
+ }
+
+ if (count == 1)
+ delete [] exists_it->second.first;
+ }
+
data_.insert(std::make_pair(atom, std::make_pair(data, size)));
}
+ui::SelectionData* SelectionFormatMap::GetFirstOf(
+ const std::vector< ::Atom>& requested_types) const {
+ for (std::vector< ::Atom>::const_iterator it = requested_types.begin();
+ it != requested_types.end(); ++it) {
+ const_iterator data_it = data_.find(*it);
+ if (data_it != data_.end()) {
+ ui::SelectionData* data = new SelectionData;
+ data->Set(data_it->first, data_it->second.first, data_it->second.second,
+ false);
+ return data;
+ }
+ }
+
+ return NULL;
+}
+
+std::vector< ::Atom> SelectionFormatMap::GetTypes() const {
+ std::vector< ::Atom> atoms;
+ for (const_iterator it = data_.begin(); it != data_.end(); ++it)
+ atoms.push_back(it->first);
+
+ return atoms;
+}
+
+scoped_ptr<SelectionFormatMap> SelectionFormatMap::Clone() const {
+ scoped_ptr<SelectionFormatMap> ret(new SelectionFormatMap);
+
+ for (const_iterator it = data_.begin(); it != data_.end(); ++it) {
+ char* data_copy = new char[it->second.second];
+ memcpy(data_copy, it->second.first, it->second.second);
+ ret->Insert(it->first, data_copy, it->second.second);
+ }
+
+ return ret.Pass();
+}
+
///////////////////////////////////////////////////////////////////////////////
-SelectionData::SelectionData(Display* x_display)
+SelectionData::SelectionData()
: type_(None),
data_(NULL),
size_(0),
owned_(false),
- atom_cache_(x_display, kSelectionDataAtoms) {
+ atom_cache_(ui::GetXDisplay(), kSelectionDataAtoms) {
}
SelectionData::~SelectionData() {
diff --git a/ui/base/x/selection_utils.h b/ui/base/x/selection_utils.h
index 741fd81..d7abf3f 100644
--- a/ui/base/x/selection_utils.h
+++ b/ui/base/x/selection_utils.h
@@ -18,6 +18,7 @@
#include "ui/base/x/x11_atom_cache.h"
namespace ui {
+class SelectionData;
class X11AtomCache;
extern const char kMimeTypeMozillaURL[];
@@ -53,6 +54,16 @@ class UI_EXPORT SelectionFormatMap {
// us.
void Insert(::Atom atom, char* data, size_t size);
+ // Returns the first of the requested_types or NULL if missing.
+ ui::SelectionData* GetFirstOf(
+ const std::vector< ::Atom>& requested_types) const;
+
+ // Returns all the selected types.
+ std::vector< ::Atom> GetTypes() const;
+
+ // Creates a copy of the selection data.
+ scoped_ptr<SelectionFormatMap> Clone() const;
+
// Pass through to STL map. Only allow non-mutation access.
const_iterator begin() const { return data_.begin(); }
const_iterator end() const { return data_.end(); }
@@ -71,7 +82,7 @@ class UI_EXPORT SelectionFormatMap {
class UI_EXPORT SelectionData {
public:
// |atom_cache| is still owned by caller.
- explicit SelectionData(Display* x_display);
+ SelectionData();
~SelectionData();
::Atom type() const { return type_; }
diff --git a/ui/base/x/x11_util.cc b/ui/base/x/x11_util.cc
index 4d06195..1ac4513 100644
--- a/ui/base/x/x11_util.cc
+++ b/ui/base/x/x11_util.cc
@@ -761,6 +761,62 @@ bool PropertyExists(XID window, const std::string& property_name) {
return num_items > 0;
}
+bool GetRawBytesOfProperty(XID window,
+ Atom property,
+ unsigned char** out_data,
+ size_t* out_data_bytes,
+ size_t* out_data_items,
+ Atom* out_type) {
+ // 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(GetXDisplay(), window, 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;
+ else
+ XFree(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;
+}
+
bool GetIntProperty(XID window, const std::string& property_name, int* value) {
Atom type = None;
int format = 0; // size in bits of each item in 'property'
@@ -782,6 +838,27 @@ bool GetIntProperty(XID window, const std::string& property_name, int* value) {
return true;
}
+bool GetXIDProperty(XID window, const std::string& property_name, XID* value) {
+ Atom type = None;
+ int format = 0; // size in bits of each item in 'property'
+ unsigned long num_items = 0;
+ unsigned char* property = NULL;
+
+ int result = GetProperty(window, property_name, 1,
+ &type, &format, &num_items, &property);
+ if (result != Success)
+ return false;
+
+ if (format != 32 || num_items != 1) {
+ XFree(property);
+ return false;
+ }
+
+ *value = *(reinterpret_cast<XID*>(property));
+ XFree(property);
+ return true;
+}
+
bool GetIntArrayProperty(XID window,
const std::string& property_name,
std::vector<int>* value) {
@@ -892,6 +969,32 @@ bool SetIntArrayProperty(XID window,
return gdk_error_trap_pop() == 0;
}
+bool SetAtomArrayProperty(XID window,
+ const std::string& name,
+ const std::string& type,
+ const std::vector<Atom>& value) {
+ DCHECK(!value.empty());
+ Atom name_atom = GetAtom(name.c_str());
+ Atom type_atom = GetAtom(type.c_str());
+
+ // XChangeProperty() expects values of type 32 to be longs.
+ scoped_ptr<Atom[]> data(new Atom[value.size()]);
+ for (size_t i = 0; i < value.size(); ++i)
+ data[i] = value[i];
+
+ gdk_error_trap_push();
+ XChangeProperty(ui::GetXDisplay(),
+ window,
+ name_atom,
+ type_atom,
+ 32, // size in bits of items in 'value'
+ PropModeReplace,
+ reinterpret_cast<const unsigned char*>(data.get()),
+ value.size()); // num items
+ XSync(ui::GetXDisplay(), False);
+ return gdk_error_trap_pop() == 0;
+}
+
Atom GetAtom(const char* name) {
#if defined(TOOLKIT_GTK)
return gdk_x11_get_xatom_by_name_for_display(
diff --git a/ui/base/x/x11_util.h b/ui/base/x/x11_util.h
index 940875a..592e48a 100644
--- a/ui/base/x/x11_util.h
+++ b/ui/base/x/x11_util.h
@@ -164,6 +164,15 @@ UI_EXPORT bool WindowContainsPoint(XID window, gfx::Point screen_loc);
// Return true if |window| has any property with |property_name|.
UI_EXPORT bool PropertyExists(XID window, const std::string& property_name);
+// Returns the raw bytes from a property with minimal
+// interpretation. |out_data| should be freed by XFree() after use.
+UI_EXPORT bool GetRawBytesOfProperty(XID window,
+ Atom property,
+ unsigned char** out_data,
+ size_t* out_data_bytes,
+ size_t* out_data_items,
+ Atom* out_type);
+
// Get the value of an int, int array, atom array or string property. On
// success, true is returned and the value is stored in |value|.
//
@@ -171,6 +180,8 @@ UI_EXPORT bool PropertyExists(XID window, const std::string& property_name);
// should accept an Atom instead of a string.
UI_EXPORT bool GetIntProperty(XID window, const std::string& property_name,
int* value);
+UI_EXPORT bool GetXIDProperty(XID window, const std::string& property_name,
+ XID* value);
UI_EXPORT bool GetIntArrayProperty(XID window, const std::string& property_name,
std::vector<int>* value);
UI_EXPORT bool GetAtomArrayProperty(XID window,
@@ -179,6 +190,7 @@ UI_EXPORT bool GetAtomArrayProperty(XID window,
UI_EXPORT bool GetStringProperty(
XID window, const std::string& property_name, std::string* value);
+// These setters all make round trips.
UI_EXPORT bool SetIntProperty(XID window,
const std::string& name,
const std::string& type,
@@ -187,6 +199,10 @@ UI_EXPORT bool SetIntArrayProperty(XID window,
const std::string& name,
const std::string& type,
const std::vector<int>& value);
+UI_EXPORT bool SetAtomArrayProperty(XID window,
+ const std::string& name,
+ const std::string& type,
+ const std::vector<Atom>& value);
// Gets the X atom for default display corresponding to atom_name.
Atom GetAtom(const char* atom_name);
diff --git a/ui/ui.gyp b/ui/ui.gyp
index 4540630..48c1441 100644
--- a/ui/ui.gyp
+++ b/ui/ui.gyp
@@ -153,7 +153,6 @@
'base/default_theme_provider_mac.mm',
'base/dragdrop/cocoa_dnd_util.h',
'base/dragdrop/cocoa_dnd_util.mm',
- 'base/dragdrop/desktop_selection_provider_aurax11.h',
'base/dragdrop/drag_drop_types.h',
'base/dragdrop/drag_drop_types_win.cc',
'base/dragdrop/drag_source_win.cc',
diff --git a/ui/views/controls/textfield/native_textfield_views_unittest.cc b/ui/views/controls/textfield/native_textfield_views_unittest.cc
index 2af45cc..56cd1d6 100644
--- a/ui/views/controls/textfield/native_textfield_views_unittest.cc
+++ b/ui/views/controls/textfield/native_textfield_views_unittest.cc
@@ -934,14 +934,7 @@ TEST_F(NativeTextfieldViewsTest, DragAndDrop_AcceptDrop) {
}
#endif
-// TODO(erg): Disabled while the other half of drag and drop is being written.
-// http://crbug.com/130806
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-#define MAYBE_DragAndDrop_InitiateDrag DISABLED_DragAndDrop_InitiateDrag
-#else
-#define MAYBE_DragAndDrop_InitiateDrag DragAndDrop_InitiateDrag
-#endif
-TEST_F(NativeTextfieldViewsTest, MAYBE_DragAndDrop_InitiateDrag) {
+TEST_F(NativeTextfieldViewsTest, DragAndDrop_InitiateDrag) {
InitTextfield(Textfield::STYLE_DEFAULT);
textfield_->SetText(ASCIIToUTF16("hello string world"));
@@ -987,14 +980,7 @@ TEST_F(NativeTextfieldViewsTest, MAYBE_DragAndDrop_InitiateDrag) {
textfield_view_->GetDragOperationsForView(textfield_view_, kStringPoint));
}
-// TODO(erg): Disabled while the other half of drag and drop is being written.
-// http://crbug.com/130806
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-#define MAYBE_DragAndDrop_ToTheRight DISABLED_DragAndDrop_ToTheRight
-#else
-#define MAYBE_DragAndDrop_ToTheRight DragAndDrop_ToTheRight
-#endif
-TEST_F(NativeTextfieldViewsTest, MAYBE_DragAndDrop_ToTheRight) {
+TEST_F(NativeTextfieldViewsTest, DragAndDrop_ToTheRight) {
InitTextfield(Textfield::STYLE_DEFAULT);
textfield_->SetText(ASCIIToUTF16("hello world"));
@@ -1049,14 +1035,7 @@ TEST_F(NativeTextfieldViewsTest, MAYBE_DragAndDrop_ToTheRight) {
EXPECT_STR_EQ("h welloorld", textfield_->text());
}
-// TODO(erg): Disabled while the other half of drag and drop is being written.
-// http://crbug.com/130806
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-#define MAYBE_DragAndDrop_ToTheLeft DISABLED_DragAndDrop_ToTheLeft
-#else
-#define MAYBE_DragAndDrop_ToTheLeft DragAndDrop_ToTheLeft
-#endif
-TEST_F(NativeTextfieldViewsTest, MAYBE_DragAndDrop_ToTheLeft) {
+TEST_F(NativeTextfieldViewsTest, DragAndDrop_ToTheLeft) {
InitTextfield(Textfield::STYLE_DEFAULT);
textfield_->SetText(ASCIIToUTF16("hello world"));
@@ -1111,14 +1090,7 @@ TEST_F(NativeTextfieldViewsTest, MAYBE_DragAndDrop_ToTheLeft) {
EXPECT_STR_EQ("h worlellod", textfield_->text());
}
-// TODO(erg): Disabled while the other half of drag and drop is being written.
-// http://crbug.com/130806
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-#define MAYBE_DragAndDrop_Canceled DISABLED_DragAndDrop_Canceled
-#else
-#define MAYBE_DragAndDrop_Canceled DragAndDrop_Canceled
-#endif
-TEST_F(NativeTextfieldViewsTest, MAYBE_DragAndDrop_Canceled) {
+TEST_F(NativeTextfieldViewsTest, DragAndDrop_Canceled) {
InitTextfield(Textfield::STYLE_DEFAULT);
textfield_->SetText(ASCIIToUTF16("hello world"));
diff --git a/ui/views/views.gyp b/ui/views/views.gyp
index 318f8db..4022df3 100644
--- a/ui/views/views.gyp
+++ b/ui/views/views.gyp
@@ -532,6 +532,7 @@
}],
['use_aura==1 and OS=="linux" and chromeos==0', {
'dependencies': [
+ '../ui.gyp:shell_dialogs',
'../linux_ui/linux_ui.gyp:linux_ui',
],
}],
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc
index ef2d6f8..004c19c 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc
@@ -16,6 +16,7 @@
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
#include "ui/base/events/event.h"
+#include "ui/base/x/selection_utils.h"
#include "ui/base/x/x11_util.h"
#include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h"
@@ -24,11 +25,20 @@ using ui::OSExchangeData;
namespace {
+const int kMinXdndVersion = 5;
+
+const int kWillAcceptDrop = 1;
+const int kWantFurtherPosEvents = 2;
+
const char kXdndActionCopy[] = "XdndActionCopy";
const char kXdndActionMove[] = "XdndActionMove";
const char kXdndActionLink[] = "XdndActionLink";
+const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER";
+const char kXdndSelection[] = "XdndSelection";
+
const char* kAtomsToCache[] = {
+ kChromiumDragReciever,
"XdndActionAsk",
kXdndActionCopy,
kXdndActionLink,
@@ -42,24 +52,117 @@ const char* kAtomsToCache[] = {
"XdndLeave",
"XdndPosition",
"XdndProxy", // Proxy windows?
- "XdndSelection",
+ kXdndSelection,
"XdndStatus",
"XdndTypeList",
NULL
};
+// Helper class to FindWindowFor which looks for a drag target under the
+// cursor.
+class DragTargetWindowFinder : public ui::EnumerateWindowsDelegate {
+ public:
+ DragTargetWindowFinder(XID ignored_icon_window,
+ gfx::Point screen_loc)
+ : ignored_icon_window_(ignored_icon_window),
+ output_window_(None),
+ screen_loc_(screen_loc) {
+ ui::EnumerateTopLevelWindows(this);
+ }
+
+ virtual ~DragTargetWindowFinder() {}
+
+ XID window() const { return output_window_; }
+
+ protected:
+ virtual bool ShouldStopIterating(XID window) OVERRIDE {
+ if (window == ignored_icon_window_)
+ return false;
+
+ if (!ui::IsWindowVisible(window))
+ return false;
+
+ if (!ui::WindowContainsPoint(window, screen_loc_))
+ return false;
+
+ if (ui::PropertyExists(window, "WM_STATE")) {
+ output_window_ = window;
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ XID ignored_icon_window_;
+ XID output_window_;
+ gfx::Point screen_loc_;
+
+ DISALLOW_COPY_AND_ASSIGN(DragTargetWindowFinder);
+};
+
+// Returns the topmost X11 window at |screen_point| if it is advertising that
+// is supports the Xdnd protocol. Will return the window under the pointer as
+// |mouse_window|. If there's a Xdnd aware window, it will be returned in
+// |dest_window|.
+void FindWindowFor(const gfx::Point& screen_point,
+ ::Window* mouse_window, ::Window* dest_window) {
+ DragTargetWindowFinder finder(None, screen_point);
+ *mouse_window = finder.window();
+ *dest_window = None;
+
+ if (finder.window() == None)
+ return;
+
+ // Figure out which window we should test as XdndAware. If mouse_window has
+ // XdndProxy, it will set that proxy on target, and if not, |target|'s
+ // original value will remain.
+ XID target = *mouse_window;
+ ui::GetXIDProperty(*mouse_window, "XdndProxy", &target);
+
+ int version;
+ if (ui::GetIntProperty(target, "XdndAware", &version) &&
+ version >= kMinXdndVersion) {
+ *dest_window = target;
+ }
+}
+
} // namespace
namespace views {
+std::map< ::Window, DesktopDragDropClientAuraX11*>
+ DesktopDragDropClientAuraX11::g_live_client_map;
+
class DesktopDragDropClientAuraX11::X11DragContext :
public base::MessageLoop::Dispatcher {
public:
X11DragContext(ui::X11AtomCache* atom_cache,
+ ::Window local_window,
const XClientMessageEvent& event);
virtual ~X11DragContext();
- const std::vector<Atom>& targets() { return targets_; }
+ // When we receive an XdndPosition message, we need to have all the data
+ // copied from the other window before we process the XdndPosition
+ // message. If we have that data already, dispatch immediately. Otherwise,
+ // delay dispatching until we do.
+ void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client,
+ ::Window source_window,
+ const gfx::Point& screen_point);
+
+ // Called to request the next target from the source window. This is only
+ // done on the first XdndPosition; after that, we cache the data offered by
+ // the source window.
+ void RequestNextTarget();
+
+ // Called when XSelection data has been copied to our process.
+ void OnSelectionNotify(const XSelectionEvent& xselection);
+
+ // Clones the fetched targets.
+ scoped_ptr<ui::SelectionFormatMap> CloneFetchedTargets() {
+ DCHECK(fetched_targets_);
+ return fetched_targets_->Clone();
+ }
// Reads the "XdndActionList" property from |source_window| and copies it
// into |actions|.
@@ -76,13 +179,30 @@ class DesktopDragDropClientAuraX11::X11DragContext :
// The atom cache owned by our parent.
ui::X11AtomCache* atom_cache_;
+ // The XID of our chrome local aura window handling our events.
+ ::Window local_window_;
+
// The XID of the window that's initiated the drag.
unsigned long source_window_;
- // targets.
- std::vector<Atom> targets_;
+ // The client we inform once we're done with requesting data.
+ DesktopDragDropClientAuraX11* drag_drop_client_;
+
+ // Whether we're blocking the handling of an XdndPosition message by waiting
+ // for |unfetched_targets_| to be fetched.
+ bool waiting_to_handle_position_;
+
+ // Where the cursor is on screen.
+ gfx::Point screen_point_;
- // supplied actions
+ // A SelectionFormatMap of data that we have in our process.
+ scoped_ptr<ui::SelectionFormatMap> fetched_targets_;
+
+ // The names of various data types offered by the other window that we
+ // haven't fetched and put in |fetched_targets_| yet.
+ std::vector<Atom> unfetched_targets_;
+
+ // Possible actions.
std::vector<Atom> actions_;
DISALLOW_COPY_AND_ASSIGN(X11DragContext);
@@ -90,60 +210,139 @@ class DesktopDragDropClientAuraX11::X11DragContext :
DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
ui::X11AtomCache* atom_cache,
+ ::Window local_window,
const XClientMessageEvent& event)
: atom_cache_(atom_cache),
- source_window_(event.data.l[0]) {
+ local_window_(local_window),
+ source_window_(event.data.l[0]),
+ drag_drop_client_(NULL),
+ waiting_to_handle_position_(false) {
bool get_types = ((event.data.l[1] & 1) != 0);
if (get_types) {
if (!ui::GetAtomArrayProperty(source_window_,
"XdndTypeList",
- &targets_)) {
+ &unfetched_targets_)) {
return;
}
} else {
// data.l[2,3,4] contain the first three types. Unused slots can be None.
for (int i = 0; i < 3; ++i) {
if (event.data.l[2+i] != None) {
- targets_.push_back(event.data.l[2+i]);
+ unfetched_targets_.push_back(event.data.l[2+i]);
}
}
}
- // TODO(erg): If this window is part of our process, don't listen through the
- // MessagePump, but instead listen to the DesktopDragDropClientAuraX11
- // associated with that window.
- base::MessagePumpAuraX11::Current()->AddDispatcherForWindow(
- this, source_window_);
- XSelectInput(base::MessagePumpAuraX11::GetDefaultXDisplay(),
- source_window_, PropertyChangeMask);
-
- // We must perform a full sync here because we could be racing
- // |source_window_|.
- XSync(base::MessagePumpAuraX11::GetDefaultXDisplay(), False);
+ DesktopDragDropClientAuraX11* client =
+ DesktopDragDropClientAuraX11::GetForWindow(source_window_);
+ if (!client) {
+ // The window doesn't have a DesktopDragDropClientAuraX11, that means it's
+ // created by some other process. Listen for messages on it.
+ base::MessagePumpAuraX11::Current()->AddDispatcherForWindow(
+ this, source_window_);
+ XSelectInput(base::MessagePumpAuraX11::GetDefaultXDisplay(),
+ source_window_, PropertyChangeMask);
+
+ // We must perform a full sync here because we could be racing
+ // |source_window_|.
+ XSync(base::MessagePumpAuraX11::GetDefaultXDisplay(), False);
+ } else {
+ // This drag originates from an aura window within our process. This means
+ // that we can shortcut the X11 server and ask the owning SelectionOwner
+ // for the data it's offering.
+ fetched_targets_ = client->CloneFormatMap();
+ unfetched_targets_.clear();
+ }
ReadActions();
}
DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() {
- // Unsubscribe to message events.
- base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(
- source_window_);
+ DesktopDragDropClientAuraX11* client =
+ DesktopDragDropClientAuraX11::GetForWindow(source_window_);
+ if (!client) {
+ // Unsubscribe from message events.
+ base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(
+ source_window_);
+ }
}
-void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
- std::vector<Atom> atom_array;
+void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage(
+ DesktopDragDropClientAuraX11* client,
+ ::Window source_window,
+ const gfx::Point& screen_point) {
+ DCHECK_EQ(source_window_, source_window);
+
+ if (!unfetched_targets_.empty()) {
+ // We have unfetched targets. That means we need to pause the handling of
+ // the position message and ask the other window for its data.
+ screen_point_ = screen_point;
+ drag_drop_client_ = client;
+ waiting_to_handle_position_ = true;
+
+ fetched_targets_.reset(new ui::SelectionFormatMap);
+ RequestNextTarget();
+ } else {
+ client->CompleteXdndPosition(source_window, screen_point);
+ }
+}
+
+void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() {
+ ::Atom target = unfetched_targets_.back();
+ unfetched_targets_.pop_back();
+
+ XConvertSelection(base::MessagePumpAuraX11::GetDefaultXDisplay(),
+ atom_cache_->GetAtom(kXdndSelection),
+ target,
+ atom_cache_->GetAtom(kChromiumDragReciever),
+ local_window_,
+ CurrentTime);
+}
+
+void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify(
+ const XSelectionEvent& event) {
+ DCHECK(waiting_to_handle_position_);
+ DCHECK(drag_drop_client_);
+ DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever));
+
+ unsigned char* data = NULL;
+ size_t data_bytes = 0;
+ ::Atom type = None;
+ if (ui::GetRawBytesOfProperty(local_window_, event.property,
+ &data, &data_bytes, NULL, &type)) {
+ char* copied_data = new char[data_bytes];
+ memcpy(copied_data, data, data_bytes);
+ fetched_targets_->Insert(event.target, copied_data, data_bytes);
+ XFree(data);
+ }
- // TODO(erg): The GTK+ code has a fast path that short circuits talking over
- // X11 for local windows. When we become a drop source, we should have a
- // similar fast path.
+ if (!unfetched_targets_.empty()) {
+ RequestNextTarget();
+ } else {
+ waiting_to_handle_position_ = false;
+ drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_);
+ drag_drop_client_ = NULL;
+ }
+}
- if (!ui::GetAtomArrayProperty(source_window_,
- "XdndActionList",
- &atom_array)) {
- actions_.clear();
+void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
+ DesktopDragDropClientAuraX11* client =
+ DesktopDragDropClientAuraX11::GetForWindow(source_window_);
+ if (!client) {
+ std::vector<Atom> atom_array;
+ if (!ui::GetAtomArrayProperty(source_window_,
+ "XdndActionList",
+ &atom_array)) {
+ actions_.clear();
+ } else {
+ actions_.swap(atom_array);
+ }
} else {
- actions_.swap(atom_array);
+ // We have a property notify set up for other windows in case they change
+ // their action list. Thankfully, the views interface is static and you
+ // can't change the action list after you enter StartDragAndDrop().
+ actions_ = client->GetOfferedDragOperations();
}
}
@@ -178,22 +377,40 @@ DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
aura::RootWindow* root_window,
Display* xdisplay,
::Window xwindow)
- : root_window_host_(root_window_host),
+ : move_loop_(this),
+ root_window_host_(root_window_host),
root_window_(root_window),
xdisplay_(xdisplay),
xwindow_(xwindow),
atom_cache_(xdisplay_, kAtomsToCache),
target_window_(NULL),
+ source_provider_(NULL),
+ source_current_window_(None),
drag_drop_in_progress_(false),
- drag_operation_(0) {
+ drag_operation_(0),
+ resulting_operation_(0) {
+ DCHECK(g_live_client_map.find(xwindow) == g_live_client_map.end());
+ g_live_client_map.insert(std::make_pair(xwindow, this));
+
// Mark that we are aware of drag and drop concepts.
- unsigned long xdnd_version = 5;
+ unsigned long xdnd_version = kMinXdndVersion;
XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"),
XA_ATOM, 32, PropModeReplace,
reinterpret_cast<unsigned char*>(&xdnd_version), 1);
}
DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() {
+ g_live_client_map.erase(xwindow_);
+}
+
+// static
+DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow(
+ ::Window window) {
+ std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it =
+ g_live_client_map.find(window);
+ if (it == g_live_client_map.end())
+ return NULL;
+ return it->second;
}
void DesktopDragDropClientAuraX11::OnXdndEnter(
@@ -207,8 +424,9 @@ void DesktopDragDropClientAuraX11::OnXdndEnter(
}
// Make sure that we've run ~X11DragContext() before creating another one.
- current_context_.reset();
- current_context_.reset(new X11DragContext(&atom_cache_, event));
+ target_current_context_.reset();
+ target_current_context_.reset(
+ new X11DragContext(&atom_cache_, xwindow_, event));
// In the Windows implementation, we immediately call DesktopDropTargetWin::
// Translate(). Here, we wait until we receive an XdndPosition message
@@ -218,8 +436,9 @@ void DesktopDragDropClientAuraX11::OnXdndEnter(
void DesktopDragDropClientAuraX11::OnXdndLeave(
const XClientMessageEvent& event) {
+ DVLOG(1) << "XdndLeave";
NotifyDragLeave();
- current_context_.reset();
+ target_current_context_.reset();
}
void DesktopDragDropClientAuraX11::OnXdndPosition(
@@ -230,48 +449,60 @@ void DesktopDragDropClientAuraX11::OnXdndPosition(
int x_root_window = event.data.l[2] >> 16;
int y_root_window = event.data.l[2] & 0xffff;
- if (!current_context_.get()) {
+ if (!target_current_context_.get()) {
NOTREACHED();
return;
}
- int drag_operation = ui::DragDropTypes::DRAG_NONE;
- scoped_ptr<ui::OSExchangeData> data;
- scoped_ptr<ui::DropTargetEvent> drop_target_event;
- DragDropDelegate* delegate;
- DragTranslate(gfx::Point(x_root_window, y_root_window),
- &data, &drop_target_event, &delegate);
- if (delegate)
- drag_operation = delegate->OnDragUpdated(*drop_target_event);
-
- // Sends an XdndStatus message back to the source_window. l[2,3]
- // theoretically represent an area in the window where the current action is
- // the same as what we're returning, but I can't find any implementation that
- // actually making use of this. A client can return (0, 0) and/or set the
- // first bit of l[1] to disable the feature, and it appears that gtk neither
- // sets this nor respects it if set.
- XEvent xev;
- xev.xclient.type = ClientMessage;
- xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus");
- xev.xclient.format = 32;
- xev.xclient.window = source_window;
- xev.xclient.data.l[0] = xwindow_;
- xev.xclient.data.l[1] = (drag_operation != 0) ? (2 | 1) : 0;
- xev.xclient.data.l[2] = 0;
- xev.xclient.data.l[3] = 0;
- xev.xclient.data.l[4] = DragOperationToAtom(drag_operation);
-
- SendXClientEvent(source_window, &xev);
+ // If we already have all the data from this drag, we complete it
+ // immediately.
+ target_current_context_->OnStartXdndPositionMessage(
+ this, source_window, gfx::Point(x_root_window, y_root_window));
}
void DesktopDragDropClientAuraX11::OnXdndStatus(
const XClientMessageEvent& event) {
DVLOG(1) << "XdndStatus";
+
+ unsigned long source_window = event.data.l[0];
+ if (event.data.l[1] & 1)
+ negotiated_operation_[source_window] = event.data.l[4];
+
+ // Note: event.data.[2,3] specify a rectangle. It is a request by the other
+ // window to not send further XdndPosition messages while the cursor is
+ // within it. However, it is considered advisory and (at least according to
+ // the spec) the other side must handle further position messages within
+ // it. GTK+ doesn't bother with this, so neither should we.
+
+ waiting_on_status_.erase(source_window);
+
+ // TODO(erg): We should be using the response to try to update the cursor or
+ // something.
+
+ if (ContainsKey(pending_drop_, source_window)) {
+ // We were waiting on the status message so we could send the XdndDrop.
+ SendXdndDrop(source_window);
+ return;
+ }
+
+ NextPositionMap::iterator it = next_position_message_.find(source_window);
+ if (it != next_position_message_.end()) {
+ // We were waiting on the status message so we could send off the next
+ // position message we queued up.
+ gfx::Point p = it->second.first;
+ unsigned long time = it->second.second;
+ next_position_message_.erase(it);
+
+ SendXdndPosition(source_window, p, time);
+ }
}
void DesktopDragDropClientAuraX11::OnXdndFinished(
const XClientMessageEvent& event) {
DVLOG(1) << "XdndFinished";
+ resulting_operation_ = AtomToDragOperation(
+ negotiated_operation_[event.data.l[0]]);
+ move_loop_.EndMoveLoop();
}
void DesktopDragDropClientAuraX11::OnXdndDrop(
@@ -286,11 +517,12 @@ void DesktopDragDropClientAuraX11::OnXdndDrop(
aura::client::GetDragDropDelegate(target_window_);
if (delegate) {
ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11(
- root_window_host_, xwindow_, current_context_->targets()));
+ xwindow_, target_current_context_->CloneFetchedTargets()));
+
ui::DropTargetEvent event(data,
target_window_location_,
target_window_root_location_,
- current_context_->GetDragOperation());
+ target_current_context_->GetDragOperation());
drag_operation = delegate->OnPerformDrop(event);
}
@@ -310,6 +542,16 @@ void DesktopDragDropClientAuraX11::OnXdndDrop(
SendXClientEvent(source_window, &xev);
}
+void DesktopDragDropClientAuraX11::OnSelectionNotify(
+ const XSelectionEvent& xselection) {
+ if (!target_current_context_) {
+ NOTIMPLEMENTED();
+ return;
+ }
+
+ target_current_context_->OnSelectionNotify(xselection);
+}
+
int DesktopDragDropClientAuraX11::StartDragAndDrop(
const ui::OSExchangeData& data,
aura::RootWindow* root_window,
@@ -317,16 +559,31 @@ int DesktopDragDropClientAuraX11::StartDragAndDrop(
const gfx::Point& root_location,
int operation,
ui::DragDropTypes::DragEventSource source) {
+ source_current_window_ = None;
+ drag_drop_in_progress_ = true;
+ drag_operation_ = operation;
+ resulting_operation_ = ui::DragDropTypes::DRAG_NONE;
+
+ const ui::OSExchangeData::Provider* provider = &data.provider();
+ source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>(
+ provider);
+
+ source_provider_->TakeOwnershipOfSelection();
+
+ std::vector< ::Atom> actions = GetOfferedDragOperations();
+ ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions);
+
// Windows has a specific method, DoDragDrop(), which performs the entire
// drag. We have to emulate this, so we spin off a nested runloop which will
// track all cursor movement and reroute events to a specific handler.
+ move_loop_.RunMoveLoop(source_window);
- NOTIMPLEMENTED();
-
- // TODO(erg): Once this is implemented, make sure to reenable the
- // NativeTextfieldViewsTest_DragAndDrop* tests.
+ source_provider_ = NULL;
+ drag_drop_in_progress_ = false;
+ drag_operation_ = 0;
+ XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList"));
- return ui::DragDropTypes::DRAG_NONE;
+ return resulting_operation_;
}
void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target,
@@ -340,7 +597,7 @@ void DesktopDragDropClientAuraX11::Drop(aura::Window* target,
}
void DesktopDragDropClientAuraX11::DragCancel() {
- NOTIMPLEMENTED();
+ move_loop_.EndMoveLoop();
}
bool DesktopDragDropClientAuraX11::IsDragDropInProgress() {
@@ -352,6 +609,64 @@ void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
target_window_ = NULL;
}
+void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) {
+ gfx::Point screen_point(event->x_root, event->y_root);
+
+ // Find the current window the cursor is over.
+ ::Window mouse_window = None;
+ ::Window dest_window = None;
+ FindWindowFor(screen_point, &mouse_window, &dest_window);
+
+ if (source_current_window_ != dest_window) {
+ if (source_current_window_ != None)
+ SendXdndLeave(source_current_window_);
+
+ source_current_window_ = dest_window;
+
+ if (source_current_window_ != None) {
+ negotiated_operation_.erase(source_current_window_);
+ SendXdndEnter(source_current_window_);
+ }
+ }
+
+ if (source_current_window_ != None) {
+ if (ContainsKey(waiting_on_status_, dest_window)) {
+ next_position_message_[dest_window] =
+ std::make_pair(screen_point, event->time);
+ } else {
+ SendXdndPosition(dest_window, screen_point, event->time);
+ }
+ }
+}
+
+void DesktopDragDropClientAuraX11::OnMouseReleased() {
+ if (source_current_window_ != None) {
+ if (ContainsKey(waiting_on_status_, source_current_window_)) {
+ // If we are waiting for an XdndStatus message, we need to wait for it to
+ // complete.
+ pending_drop_.insert(source_current_window_);
+ return;
+ }
+
+ std::map< ::Window, ::Atom>::iterator it =
+ negotiated_operation_.find(source_current_window_);
+ if (it != negotiated_operation_.end() && it->second != None) {
+ // We have negotiated an action with the other end.
+ SendXdndDrop(source_current_window_);
+ return;
+ }
+
+ SendXdndLeave(source_current_window_);
+ source_current_window_ = None;
+ }
+
+ move_loop_.EndMoveLoop();
+}
+
+void DesktopDragDropClientAuraX11::OnMoveLoopEnded() {
+ target_current_context_.reset();
+}
+
void DesktopDragDropClientAuraX11::DragTranslate(
const gfx::Point& root_window_location,
scoped_ptr<ui::OSExchangeData>* data,
@@ -378,7 +693,7 @@ void DesktopDragDropClientAuraX11::DragTranslate(
return;
data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11(
- root_window_host_, xwindow_, current_context_->targets())));
+ xwindow_, target_current_context_->CloneFetchedTargets())));
gfx::Point location = root_location;
aura::Window::ConvertPointToTarget(root_window_, target_window_, &location);
@@ -389,7 +704,7 @@ void DesktopDragDropClientAuraX11::DragTranslate(
*(data->get()),
location,
root_location,
- current_context_->GetDragOperation()));
+ target_current_context_->GetDragOperation()));
if (target_window_changed)
(*delegate)->OnDragEntered(*event->get());
}
@@ -405,7 +720,7 @@ void DesktopDragDropClientAuraX11::NotifyDragLeave() {
target_window_ = NULL;
}
-unsigned long DesktopDragDropClientAuraX11::DragOperationToAtom(
+::Atom DesktopDragDropClientAuraX11::DragOperationToAtom(
int drag_operation) {
if (drag_operation & ui::DragDropTypes::DRAG_COPY)
return atom_cache_.GetAtom(kXdndActionCopy);
@@ -417,24 +732,184 @@ unsigned long DesktopDragDropClientAuraX11::DragOperationToAtom(
return None;
}
-void DesktopDragDropClientAuraX11::SendXClientEvent(unsigned long xid,
+int DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
+ if (atom == atom_cache_.GetAtom(kXdndActionCopy))
+ return ui::DragDropTypes::DRAG_COPY;
+ if (atom == atom_cache_.GetAtom(kXdndActionMove))
+ return ui::DragDropTypes::DRAG_MOVE;
+ if (atom == atom_cache_.GetAtom(kXdndActionLink))
+ return ui::DragDropTypes::DRAG_LINK;
+
+ return ui::DragDropTypes::DRAG_NONE;
+}
+
+std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() {
+ std::vector< ::Atom> operations;
+ if (drag_operation_ & ui::DragDropTypes::DRAG_COPY)
+ operations.push_back(atom_cache_.GetAtom(kXdndActionCopy));
+ if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE)
+ operations.push_back(atom_cache_.GetAtom(kXdndActionMove));
+ if (drag_operation_ & ui::DragDropTypes::DRAG_LINK)
+ operations.push_back(atom_cache_.GetAtom(kXdndActionLink));
+ return operations;
+}
+
+scoped_ptr<ui::SelectionFormatMap>
+DesktopDragDropClientAuraX11::CloneFormatMap() const {
+ return source_provider_ ? source_provider_->CloneFormatMap() :
+ scoped_ptr<ui::SelectionFormatMap>();
+}
+
+void DesktopDragDropClientAuraX11::CompleteXdndPosition(
+ ::Window source_window,
+ const gfx::Point& screen_point) {
+ int drag_operation = ui::DragDropTypes::DRAG_NONE;
+ scoped_ptr<ui::OSExchangeData> data;
+ scoped_ptr<ui::DropTargetEvent> drop_target_event;
+ DragDropDelegate* delegate = NULL;
+ DragTranslate(screen_point, &data, &drop_target_event, &delegate);
+ if (delegate)
+ drag_operation = delegate->OnDragUpdated(*drop_target_event);
+
+ // Sends an XdndStatus message back to the source_window. l[2,3]
+ // theoretically represent an area in the window where the current action is
+ // the same as what we're returning, but I can't find any implementation that
+ // actually making use of this. A client can return (0, 0) and/or set the
+ // first bit of l[1] to disable the feature, and it appears that gtk neither
+ // sets this nor respects it if set.
+ XEvent xev;
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus");
+ xev.xclient.format = 32;
+ xev.xclient.window = source_window;
+ xev.xclient.data.l[0] = xwindow_;
+ xev.xclient.data.l[1] = (drag_operation != 0) ?
+ (kWantFurtherPosEvents | kWillAcceptDrop) : 0;
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = DragOperationToAtom(drag_operation);
+
+ SendXClientEvent(source_window, &xev);
+}
+
+void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) {
+ XEvent xev;
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter");
+ xev.xclient.format = 32;
+ xev.xclient.window = dest_window;
+ xev.xclient.data.l[0] = xwindow_;
+ xev.xclient.data.l[1] = (kMinXdndVersion << 24); // The version number.
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ std::vector<Atom> targets;
+ source_provider_->RetrieveTargets(&targets);
+
+ if (targets.size() > 3) {
+ xev.xclient.data.l[1] |= 1;
+ ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets);
+ } else {
+ // Pack the targets into the enter message.
+ for (size_t i = 0; i < targets.size(); ++i)
+ xev.xclient.data.l[2 + i] = targets[i];
+ }
+
+ SendXClientEvent(dest_window, &xev);
+}
+
+void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
+ // If we're sending a leave message, don't wait for status messages anymore.
+ waiting_on_status_.erase(dest_window);
+ NextPositionMap::iterator it = next_position_message_.find(dest_window);
+ if (it != next_position_message_.end())
+ next_position_message_.erase(it);
+
+ XEvent xev;
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave");
+ xev.xclient.format = 32;
+ xev.xclient.window = dest_window;
+ xev.xclient.data.l[0] = xwindow_;
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+ SendXClientEvent(dest_window, &xev);
+}
+
+void DesktopDragDropClientAuraX11::SendXdndPosition(
+ ::Window dest_window,
+ const gfx::Point& screen_point,
+ unsigned long time) {
+ waiting_on_status_.insert(dest_window);
+
+ XEvent xev;
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition");
+ xev.xclient.format = 32;
+ xev.xclient.window = dest_window;
+ xev.xclient.data.l[0] = xwindow_;
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y();
+ xev.xclient.data.l[3] = time;
+ xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
+ SendXClientEvent(dest_window, &xev);
+}
+
+void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {
+ XEvent xev;
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop");
+ xev.xclient.format = 32;
+ xev.xclient.window = dest_window;
+ xev.xclient.data.l[0] = xwindow_;
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = CurrentTime;
+ xev.xclient.data.l[3] = None;
+ xev.xclient.data.l[4] = None;
+ SendXClientEvent(dest_window, &xev);
+}
+
+void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid,
XEvent* xev) {
DCHECK_EQ(ClientMessage, xev->type);
- // TODO(erg): When I get drag receiving working, shortcut messages to the X
- // server and call the receivers DesktopDragDropClientAuraX11 instance
- // instead.
- //
+ // Don't send messages to the X11 message queue if we can help it.
+ DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid);
+ if (short_circuit) {
+ Atom message_type = xev->xclient.message_type;
+ if (message_type == atom_cache_.GetAtom("XdndEnter")) {
+ short_circuit->OnXdndEnter(xev->xclient);
+ return;
+ } else if (message_type == atom_cache_.GetAtom("XdndLeave")) {
+ short_circuit->OnXdndLeave(xev->xclient);
+ return;
+ } else if (message_type == atom_cache_.GetAtom("XdndPosition")) {
+ short_circuit->OnXdndPosition(xev->xclient);
+ return;
+ } else if (message_type == atom_cache_.GetAtom("XdndStatus")) {
+ short_circuit->OnXdndStatus(xev->xclient);
+ return;
+ } else if (message_type == atom_cache_.GetAtom("XdndFinished")) {
+ short_circuit->OnXdndFinished(xev->xclient);
+ return;
+ } else if (message_type == atom_cache_.GetAtom("XdndDrop")) {
+ short_circuit->OnXdndDrop(xev->xclient);
+ return;
+ }
+ }
+
// I don't understand why the GTK+ code is doing what it's doing here. It
// goes out of its way to send the XEvent so that it receives a callback on
- // success or failure, and when it fails, it then sends an internal GdkEvent
- // about the failed drag. (And sending this message doesn't appear to go
- // through normal xlib machinery, but instead passes through the low level
- // xProto (the x11 wire format) that I don't understand.
+ // success or failure, and when it fails, it then sends an internal
+ // GdkEvent about the failed drag. (And sending this message doesn't appear
+ // to go through normal xlib machinery, but instead passes through the low
+ // level xProto (the x11 wire format) that I don't understand.
//
// I'm unsure if I have to jump through those hoops, or if XSendEvent is
// sufficient.
-
XSendEvent(xdisplay_, xid, False, 0, xev);
}
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h
index 47c4f2b..4152778 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h
@@ -10,6 +10,8 @@
// Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class.
#undef RootWindow
+#include <set>
+
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
@@ -18,6 +20,8 @@
#include "ui/base/x/x11_atom_cache.h"
#include "ui/gfx/point.h"
#include "ui/views/views_export.h"
+#include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h"
+#include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h"
namespace aura {
class RootWindow;
@@ -34,7 +38,9 @@ namespace ui {
class DragSource;
class DropTargetEvent;
class OSExchangeData;
+class OSExchangeDataProviderAuraX11;
class RootWindow;
+class SelectionFormatMap;
}
namespace views {
@@ -45,7 +51,8 @@ class DesktopRootWindowHostX11;
// handles the views drag events.
class VIEWS_EXPORT DesktopDragDropClientAuraX11
: public aura::client::DragDropClient,
- public aura::WindowObserver {
+ public aura::WindowObserver,
+ public X11WholeScreenMoveLoopDelegate {
public:
DesktopDragDropClientAuraX11(
views::DesktopRootWindowHostX11* root_window_host,
@@ -54,6 +61,11 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11
::Window xwindow);
virtual ~DesktopDragDropClientAuraX11();
+ // We maintain a mapping of live DesktopDragDropClientAuraX11 objects to
+ // their ::Windows. We do this so that we're able to short circuit sending
+ // X11 messages to windows in our process.
+ static DesktopDragDropClientAuraX11* GetForWindow(::Window window);
+
// These methods handle the various X11 client messages from the platform.
void OnXdndEnter(const XClientMessageEvent& event);
void OnXdndLeave(const XClientMessageEvent& event);
@@ -62,6 +74,9 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11
void OnXdndFinished(const XClientMessageEvent& event);
void OnXdndDrop(const XClientMessageEvent& event);
+ // Called when XSelection data has been copied to our process.
+ void OnSelectionNotify(const XSelectionEvent& xselection);
+
// Overridden from aura::client::DragDropClient:
virtual int StartDragAndDrop(
const ui::OSExchangeData& data,
@@ -77,10 +92,18 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11
virtual void DragCancel() OVERRIDE;
virtual bool IsDragDropInProgress() OVERRIDE;
- // aura::WindowObserver implementation:
+ // Overridden from aura::WindowObserver:
virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
+ // Overridden from X11WholeScreenMoveLoopDelegate:
+ virtual void OnMouseMovement(XMotionEvent* event) OVERRIDE;
+ virtual void OnMouseReleased() OVERRIDE;
+ virtual void OnMoveLoopEnded() OVERRIDE;
+
private:
+ typedef std::map< ::Window, std::pair<gfx::Point, unsigned long> >
+ NextPositionMap;
+
// When we receive an position x11 message, we need to translate that into
// the underlying aura::Window representation, as moves internal to the X11
// window can cause internal drag leave and enter messages.
@@ -95,11 +118,41 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11
// Converts our bitfield of actions into an Atom that represents what action
// we're most likely to take on drop.
- unsigned long DragOperationToAtom(int drag_operation);
+ ::Atom DragOperationToAtom(int drag_operation);
+
+ // Converts a single action atom to a drag operation.
+ int AtomToDragOperation(::Atom atom);
+
+ // During the blocking StartDragAndDrop() call, this converts the views-style
+ // |drag_operation_| bitfield into a vector of Atoms to offer to other
+ // processes.
+ std::vector< ::Atom> GetOfferedDragOperations();
+
+ // This returns a newly allocated copy of the data we're offering in this
+ // drag. This is done to bypass an asynchronous roundtrip with the X11
+ // server.
+ scoped_ptr<ui::SelectionFormatMap> CloneFormatMap() const;
+
+ // Handling XdndPosition can be paused while waiting for more data; this is
+ // called either synchronously from OnXdndPosition, or asynchronously after
+ // we've received data requested from the other window.
+ void CompleteXdndPosition(::Window source_window,
+ const gfx::Point& screen_point);
+
+ void SendXdndEnter(::Window dest_window);
+ void SendXdndLeave(::Window dest_window);
+ void SendXdndPosition(::Window dest_window,
+ const gfx::Point& screen_point,
+ unsigned long time);
+ void SendXdndDrop(::Window dest_window);
// Sends |xev| to |xid|, optionally short circuiting the round trip to the X
// server.
- void SendXClientEvent(unsigned long xid, XEvent* xev);
+ void SendXClientEvent(::Window xid, XEvent* xev);
+
+ // A nested message loop that notifies this object of events through the
+ // X11WholeScreenMoveLoopDelegate interface.
+ X11WholeScreenMoveLoop move_loop_;
views::DesktopRootWindowHostX11* root_window_host_;
aura::RootWindow* root_window_;
@@ -110,9 +163,8 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11
ui::X11AtomCache atom_cache_;
// Target side information.
-
class X11DragContext;
- scoped_ptr<X11DragContext> current_context_;
+ scoped_ptr<X11DragContext> target_current_context_;
// The Aura window that is currently under the cursor. We need to manually
// keep track of this because Windows will only call our drag enter method
@@ -127,10 +179,45 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11
gfx::Point target_window_location_;
gfx::Point target_window_root_location_;
+ // In the Xdnd protocol, we aren't supposed to send another XdndPosition
+ // message until we have received a confirming XdndStatus message.
+ std::set< ::Window> waiting_on_status_;
+
+ // If we would send an XdndPosition message while we're waiting for an
+ // XdndStatus response, we need to cache the latest details we'd send.
+ NextPositionMap next_position_message_;
+
+ // Source side information.
+ ui::OSExchangeDataProviderAuraX11 const* source_provider_;
+ ::Window source_current_window_;
+
bool drag_drop_in_progress_;
+ // The operation bitfield as requested by StartDragAndDrop.
int drag_operation_;
+ // The operation performed. Is initialized to None at the start of
+ // StartDragAndDrop(), and is set only during the asynchronous XdndFinished
+ // message.
+ int resulting_operation_;
+
+ // This window will be receiving a drop as soon as we receive an XdndStatus
+ // from it.
+ std::set< ::Window> pending_drop_;
+
+ // We offer the other window a list of possible operations,
+ // XdndActionsList. This is the requested action from the other window. This
+ // is None if we haven't sent out an XdndPosition message yet, haven't yet
+ // received an XdndStatus or if the other window has told us that there's no
+ // action that we can agree on.
+ //
+ // This is a map instead of a simple variable because of the case where we
+ // put an XdndLeave in the queue at roughly the same time that the other
+ // window responds to an XdndStatus.
+ std::map< ::Window, ::Atom> negotiated_operation_;
+
+ static std::map< ::Window, DesktopDragDropClientAuraX11*> g_live_client_map;
+
DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11);
};
diff --git a/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc b/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc
index 4606a6f..ea98a9c 100644
--- a/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc
@@ -111,8 +111,7 @@ DesktopRootWindowHostX11::DesktopRootWindowHostX11(
focus_when_shown_(false),
current_cursor_(ui::kCursorNull),
native_widget_delegate_(native_widget_delegate),
- desktop_native_widget_aura_(desktop_native_widget_aura),
- drop_handler_(NULL) {
+ desktop_native_widget_aura_(desktop_native_widget_aura) {
}
DesktopRootWindowHostX11::~DesktopRootWindowHostX11() {
@@ -907,20 +906,6 @@ void DesktopRootWindowHostX11::PrepareForShutdown() {
}
////////////////////////////////////////////////////////////////////////////////
-// DesktopRootWindowHostX11, ui::DesktopSelectionProviderAuraX11 implementation:
-
-void DesktopRootWindowHostX11::SetDropHandler(
- ui::OSExchangeDataProviderAuraX11* handler) {
- if (handler) {
- DCHECK(!drop_handler_);
- drop_handler_ = handler;
- } else {
- DCHECK(drop_handler_);
- drop_handler_ = NULL;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
// DesktopRootWindowHostX11, MessageLoop::Dispatcher implementation:
bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) {
@@ -1194,8 +1179,7 @@ bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) {
break;
}
case SelectionNotify: {
- if (drop_handler_)
- drop_handler_->OnSelectionNotify(xev->xselection);
+ drag_drop_client_->OnSelectionNotify(xev->xselection);
break;
}
}
diff --git a/ui/views/widget/desktop_aura/desktop_root_window_host_x11.h b/ui/views/widget/desktop_aura/desktop_root_window_host_x11.h
index 8cd7aa3..ca178ba 100644
--- a/ui/views/widget/desktop_aura/desktop_root_window_host_x11.h
+++ b/ui/views/widget/desktop_aura/desktop_root_window_host_x11.h
@@ -15,7 +15,6 @@
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/root_window_host.h"
#include "ui/base/cursor/cursor_loader_x11.h"
-#include "ui/base/dragdrop/desktop_selection_provider_aurax11.h"
#include "ui/base/x/x11_atom_cache.h"
#include "ui/gfx/rect.h"
#include "ui/views/views_export.h"
@@ -43,7 +42,6 @@ class CursorManager;
class VIEWS_EXPORT DesktopRootWindowHostX11 :
public DesktopRootWindowHost,
public aura::RootWindowHost,
- public ui::DesktopSelectionProviderAuraX11,
public base::MessageLoop::Dispatcher {
public:
DesktopRootWindowHostX11(
@@ -172,10 +170,6 @@ class VIEWS_EXPORT DesktopRootWindowHostX11 :
virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE;
virtual void PrepareForShutdown() OVERRIDE;
- // Overridden from DesktopSelectionProviderAuraX11:
- virtual void SetDropHandler(
- ui::OSExchangeDataProviderAuraX11* handler) OVERRIDE;
-
// Overridden from Dispatcher:
virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
@@ -230,9 +224,6 @@ class VIEWS_EXPORT DesktopRootWindowHostX11 :
aura::RootWindowHostDelegate* root_window_host_delegate_;
aura::Window* content_window_;
- // We forward drop related messages to this object.
- ui::OSExchangeDataProviderAuraX11* drop_handler_;
-
// The current root window host that has capture. While X11 has something
// like Windows SetCapture()/ReleaseCapture(), it is entirely implicit and
// there are no notifications when this changes. We need to track this so we
diff --git a/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc b/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc
index e90cb64..986e945 100644
--- a/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc
+++ b/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc
@@ -34,6 +34,10 @@ void X11DesktopWindowMoveClient::OnMouseMovement(XMotionEvent* event) {
system_loc, root_window_->GetHostSize()));
}
+void X11DesktopWindowMoveClient::OnMouseReleased() {
+ EndMoveLoop();
+}
+
void X11DesktopWindowMoveClient::OnMoveLoopEnded() {
root_window_ = NULL;
}
diff --git a/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h b/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h
index 0238a25..d4f2d8e 100644
--- a/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h
+++ b/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h
@@ -35,6 +35,7 @@ class VIEWS_EXPORT X11DesktopWindowMoveClient :
// Overridden from X11WholeScreenMoveLoopDelegate:
virtual void OnMouseMovement(XMotionEvent* event) OVERRIDE;
+ virtual void OnMouseReleased() OVERRIDE;
virtual void OnMoveLoopEnded() OVERRIDE;
// Overridden from aura::client::WindowMoveClient:
diff --git a/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc b/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc
index 7053432..9cdce13 100644
--- a/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc
+++ b/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc
@@ -43,9 +43,8 @@ bool X11WholeScreenMoveLoop::Dispatch(const base::NativeEvent& event) {
delegate_->OnMouseMovement(&xev->xmotion);
break;
}
- case ButtonPress:
case ButtonRelease: {
- EndMoveLoop();
+ delegate_->OnMouseReleased();
break;
}
}
diff --git a/ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h b/ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h
index f6a569c..69f63be 100644
--- a/ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h
+++ b/ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h
@@ -18,6 +18,9 @@ class X11WholeScreenMoveLoopDelegate {
// Called when we receive a motion event.
virtual void OnMouseMovement(XMotionEvent* event) = 0;
+ // Called when the mouse button is released.
+ virtual void OnMouseReleased() = 0;
+
// Called when the user has released the mouse button. The move loop will
// release the grab after this has been called.
virtual void OnMoveLoopEnded() = 0;