Reland "[ozone/x11] Moved core part of XDND out of DDDClientAuraX11."
This is a reland of 5ab83967a3cb2a53ebce484c7535d20b908266c2 with a fix
proposed by caseq@ in https://crrev.com/c/chromium/src/+/1962652
Original change's description:
> [ozone/x11] Moved core part of XDND out of DDDClientAuraX11.
>
> This CL is the second attempt to begin refactoring drag and drop for
> Linux platforms and uniting Ozone and non-Ozone implementations.
>
> The first attempt had been discussed in [1].
>
> The X11 drag and drop is currently implemented in DDDClientAuraX11 [2]
> that cannot be reused in Ozone. To make it reusable, we need to move
> as much as possible into some common place that doesn't depend on
> platform events.
>
> This CL moves some pieces of logic from DDDClientAuraX11 into
> //ui/base/x so it can be used later by other drag and drop clients.
>
> [1] https://crrev.com/c/chromium/src/+/1954010
> [2] views::DesktopDragDropClientAuraX11
>
> R=thomasanderson@chromium.org
>
> Bug: 1014860
> Change-Id: Ifaa81317dc5fa3da7a67d5d1c18b1626a982c794
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1960155
> Commit-Queue: Alexander Dunaev <adunaev@igalia.com>
> Reviewed-by: Thomas Anderson <thomasanderson@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#723875}
Bug: 1014860
Change-Id: I6df16fa51889a0bea3dbac29278b57604e72357f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1962258
Reviewed-by: Thomas Anderson <thomasanderson@chromium.org>
Reviewed-by: Andrey Kosyakov <caseq@chromium.org>
Commit-Queue: Alexander Dunaev <adunaev@igalia.com>
Cr-Commit-Position: refs/heads/master@{#724227}
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 224ced8..0178944c 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -471,10 +471,13 @@
deps += [ "//ui/events" ]
}
- if (use_x11) {
+ if (is_desktop_linux && (use_x11 || ozone_platform_x11)) {
public_deps += [ "//ui/base/x" ]
configs += [ "//build/config/linux:x11" ]
+ # X11 drag and drop wants to use common drag and drop types.
+ allow_circular_includes_from = [ "//ui/base/x" ]
+
if (!is_chromeos) {
configs += [ "//build/config/linux:xscrnsaver" ]
deps += [ "//ui/gfx/x" ]
@@ -492,8 +495,6 @@
"x/selection_owner.h",
"x/selection_requestor.cc",
"x/selection_requestor.h",
- "x/selection_utils.cc",
- "x/selection_utils.h",
]
deps += [
"//ui/events/platform/x11",
diff --git a/ui/base/x/BUILD.gn b/ui/base/x/BUILD.gn
index 7b8a4f8..c716bcb2 100644
--- a/ui/base/x/BUILD.gn
+++ b/ui/base/x/BUILD.gn
@@ -12,6 +12,8 @@
output_name = "ui_base_x"
sources = [
+ "selection_utils.cc",
+ "selection_utils.h",
"x11_display_manager.cc",
"x11_display_manager.h",
"x11_display_util.cc",
@@ -43,6 +45,15 @@
]
}
+ if (is_desktop_linux) {
+ sources += [
+ "x11_drag_context.cc",
+ "x11_drag_context.h",
+ "x11_drag_drop_client.cc",
+ "x11_drag_drop_client.h",
+ ]
+ }
+
configs += [
"//build/config/linux:x11",
"//build/config/linux:xrandr",
@@ -55,6 +66,7 @@
"//base:i18n",
"//skia",
"//ui/base:hit_test",
+ "//ui/base/clipboard:clipboard_types",
"//ui/display/util",
"//ui/events",
"//ui/events/devices/x11",
diff --git a/ui/base/x/selection_utils.h b/ui/base/x/selection_utils.h
index 6954294..5be34cd 100644
--- a/ui/base/x/selection_utils.h
+++ b/ui/base/x/selection_utils.h
@@ -8,47 +8,52 @@
#include <stddef.h>
#include <map>
+#include "base/component_export.h"
#include "base/memory/ref_counted_memory.h"
-#include "ui/base/ui_base_export.h"
#include "ui/gfx/x/x11.h"
namespace ui {
class SelectionData;
-UI_BASE_EXPORT extern const char kString[];
-UI_BASE_EXPORT extern const char kText[];
-UI_BASE_EXPORT extern const char kUtf8String[];
+COMPONENT_EXPORT(UI_BASE_X) extern const char kString[];
+COMPONENT_EXPORT(UI_BASE_X) extern const char kText[];
+COMPONENT_EXPORT(UI_BASE_X) extern const char kUtf8String[];
// Returns a list of all text atoms that we handle.
-UI_BASE_EXPORT std::vector<::Atom> GetTextAtomsFrom();
+COMPONENT_EXPORT(UI_BASE_X) std::vector<::Atom> GetTextAtomsFrom();
-UI_BASE_EXPORT std::vector<::Atom> GetURLAtomsFrom();
+COMPONENT_EXPORT(UI_BASE_X) std::vector<::Atom> GetURLAtomsFrom();
-UI_BASE_EXPORT std::vector<::Atom> GetURIListAtomsFrom();
+COMPONENT_EXPORT(UI_BASE_X) std::vector<::Atom> GetURIListAtomsFrom();
// Places the intersection of |desired| and |offered| into |output|.
-UI_BASE_EXPORT void GetAtomIntersection(const std::vector< ::Atom>& desired,
- const std::vector< ::Atom>& offered,
- std::vector< ::Atom>* output);
+COMPONENT_EXPORT(UI_BASE_X)
+void GetAtomIntersection(const std::vector<::Atom>& desired,
+ const std::vector<::Atom>& offered,
+ std::vector<::Atom>* output);
// Takes the raw bytes of the base::string16 and copies them into |bytes|.
-UI_BASE_EXPORT void AddString16ToVector(const base::string16& str,
- std::vector<unsigned char>* bytes);
+COMPONENT_EXPORT(UI_BASE_X)
+void AddString16ToVector(const base::string16& str,
+ std::vector<unsigned char>* bytes);
// Tokenizes and parses the Selection Data as if it is a URI List.
-UI_BASE_EXPORT std::vector<std::string> ParseURIList(const SelectionData& data);
+COMPONENT_EXPORT(UI_BASE_X)
+std::vector<std::string> ParseURIList(const SelectionData& data);
-UI_BASE_EXPORT std::string RefCountedMemoryToString(
+COMPONENT_EXPORT(UI_BASE_X)
+std::string RefCountedMemoryToString(
const scoped_refptr<base::RefCountedMemory>& memory);
-UI_BASE_EXPORT base::string16 RefCountedMemoryToString16(
+COMPONENT_EXPORT(UI_BASE_X)
+base::string16 RefCountedMemoryToString16(
const scoped_refptr<base::RefCountedMemory>& memory);
///////////////////////////////////////////////////////////////////////////////
// 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_BASE_EXPORT SelectionFormatMap {
+class COMPONENT_EXPORT(UI_BASE_X) SelectionFormatMap {
public:
// Our internal data store, which we only expose through iterators.
typedef std::map< ::Atom, scoped_refptr<base::RefCountedMemory> > InternalMap;
@@ -83,7 +88,7 @@
///////////////////////////////////////////////////////////////////////////////
// A holder for data with optional X11 deletion semantics.
-class UI_BASE_EXPORT SelectionData {
+class COMPONENT_EXPORT(UI_BASE_X) SelectionData {
public:
// |atom_cache| is still owned by caller.
SelectionData();
diff --git a/ui/base/x/x11_drag_context.cc b/ui/base/x/x11_drag_context.cc
new file mode 100644
index 0000000..3ed9e98b
--- /dev/null
+++ b/ui/base/x/x11_drag_context.cc
@@ -0,0 +1,199 @@
+// Copyright 2019 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/x11_drag_context.h"
+
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/x/x11_drag_drop_client.h"
+#include "ui/base/x/x11_util.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/gfx/x/x11_atom_cache.h"
+
+namespace ui {
+
+namespace {
+
+// Window property that holds the supported drag and drop data types.
+// This property is set on the XDND source window when the drag and drop data
+// can be converted to more than 3 types.
+const char kXdndTypeList[] = "XdndTypeList";
+
+// Selection used by the XDND protocol to transfer data between applications.
+const char kXdndSelection[] = "XdndSelection";
+
+// Window property that contains the possible actions that will be presented to
+// the user when the drag and drop action is kXdndActionAsk.
+const char kXdndActionList[] = "XdndActionList";
+
+// These actions have the same meaning as in the W3C Drag and Drop spec.
+const char kXdndActionCopy[] = "XdndActionCopy";
+const char kXdndActionMove[] = "XdndActionMove";
+const char kXdndActionLink[] = "XdndActionLink";
+
+// Window property that will receive the drag and drop selection data.
+const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER";
+
+} // namespace
+
+XDragContext::XDragContext(XID local_window,
+ const XClientMessageEvent& event,
+ XDragDropClient* source_client,
+ const SelectionFormatMap& data)
+ : local_window_(local_window),
+ source_window_(event.data.l[0]),
+ source_client_(source_client) {
+ if (!source_client_) {
+ bool get_types_from_property = ((event.data.l[1] & 1) != 0);
+
+ if (get_types_from_property) {
+ if (!GetAtomArrayProperty(source_window_, kXdndTypeList,
+ &unfetched_targets_)) {
+ return;
+ }
+ } else {
+ // data.l[2,3,4] contain the first three types. Unused slots can be None.
+ for (size_t i = 2; i < 5; ++i) {
+ if (event.data.l[i] != x11::None)
+ unfetched_targets_.push_back(event.data.l[i]);
+ }
+ }
+
+#if DCHECK_IS_ON()
+ DVLOG(1) << "XdndEnter has " << unfetched_targets_.size() << " data types";
+ for (Atom target : unfetched_targets_)
+ DVLOG(1) << "XdndEnter data type: " << target;
+#endif // DCHECK_IS_ON()
+
+ // We must perform a full sync here because we could be racing
+ // |source_window_|.
+ XSync(gfx::GetXDisplay(), x11::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_ = data;
+ }
+
+ ReadActions();
+}
+
+XDragContext::~XDragContext() = default;
+
+void XDragContext::OnXdndPositionMessage(XDragDropClient* client,
+ Atom suggested_action,
+ XID source_window,
+ Time time_stamp,
+ const gfx::Point& screen_point) {
+ DCHECK_EQ(source_window_, source_window);
+ suggested_action_ = suggested_action;
+
+ 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;
+ position_time_stamp_ = time_stamp;
+ waiting_to_handle_position_ = true;
+
+ fetched_targets_ = SelectionFormatMap();
+ RequestNextTarget();
+ } else {
+ client->CompleteXdndPosition(source_window, screen_point);
+ }
+}
+
+void XDragContext::RequestNextTarget() {
+ DCHECK(!unfetched_targets_.empty());
+ DCHECK(drag_drop_client_);
+ DCHECK(waiting_to_handle_position_);
+
+ Atom target = unfetched_targets_.back();
+ unfetched_targets_.pop_back();
+
+ XConvertSelection(gfx::GetXDisplay(), gfx::GetAtom(kXdndSelection), target,
+ gfx::GetAtom(kChromiumDragReciever), local_window_,
+ position_time_stamp_);
+}
+
+void XDragContext::OnSelectionNotify(const XSelectionEvent& event) {
+ if (!waiting_to_handle_position_) {
+ // A misbehaved window may send SelectionNotify without us requesting data
+ // via XConvertSelection().
+ return;
+ }
+ DCHECK(drag_drop_client_);
+
+ DVLOG(1) << "SelectionNotify, format " << event.target;
+
+ if (event.property != x11::None) {
+ DCHECK_EQ(event.property, gfx::GetAtom(kChromiumDragReciever));
+
+ scoped_refptr<base::RefCountedMemory> data;
+ Atom type = x11::None;
+ if (GetRawBytesOfProperty(local_window_, event.property, &data, nullptr,
+ &type)) {
+ fetched_targets_.Insert(event.target, data);
+ }
+ } else {
+ // The source failed to convert the drop data to the format (target in X11
+ // parlance) that we asked for. This happens, even though we only ask for
+ // the formats advertised by the source. http://crbug.com/628099
+ LOG(ERROR) << "XConvertSelection failed for source-advertised target "
+ << event.target;
+ }
+
+ if (!unfetched_targets_.empty()) {
+ RequestNextTarget();
+ } else {
+ waiting_to_handle_position_ = false;
+ drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_);
+ drag_drop_client_ = nullptr;
+ }
+}
+
+void XDragContext::ReadActions() {
+ if (!source_client_) {
+ std::vector<Atom> atom_array;
+ if (!GetAtomArrayProperty(source_window_, kXdndActionList, &atom_array))
+ actions_.clear();
+ else
+ actions_.swap(atom_array);
+ } else {
+ // 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_ = source_client_->GetOfferedDragOperations();
+ }
+}
+
+int XDragContext::GetDragOperation() const {
+ int drag_operation = DragDropTypes::DRAG_NONE;
+ for (const auto& action : actions_)
+ MaskOperation(action, &drag_operation);
+
+ MaskOperation(suggested_action_, &drag_operation);
+
+ return drag_operation;
+}
+
+void XDragContext::MaskOperation(Atom xdnd_operation,
+ int* drag_operation) const {
+ if (xdnd_operation == gfx::GetAtom(kXdndActionCopy))
+ *drag_operation |= DragDropTypes::DRAG_COPY;
+ else if (xdnd_operation == gfx::GetAtom(kXdndActionMove))
+ *drag_operation |= DragDropTypes::DRAG_MOVE;
+ else if (xdnd_operation == gfx::GetAtom(kXdndActionLink))
+ *drag_operation |= DragDropTypes::DRAG_LINK;
+}
+
+bool XDragContext::DispatchXEvent(XEvent* xev) {
+ if (xev->type == PropertyNotify &&
+ xev->xproperty.atom == gfx::GetAtom(kXdndActionList)) {
+ ReadActions();
+ return true;
+ }
+ return false;
+}
+
+} // namespace ui
diff --git a/ui/base/x/x11_drag_context.h b/ui/base/x/x11_drag_context.h
new file mode 100644
index 0000000..386ca281
--- /dev/null
+++ b/ui/base/x/x11_drag_context.h
@@ -0,0 +1,111 @@
+// Copyright 2019 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_X11_DRAG_CONTEXT_H_
+#define UI_BASE_X_X11_DRAG_CONTEXT_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/component_export.h"
+#include "ui/base/x/selection_utils.h"
+#include "ui/events/platform/x11/x11_event_source.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/x/x11.h"
+
+namespace ui {
+
+class XDragDropClient;
+
+class COMPONENT_EXPORT(UI_BASE_X) XDragContext {
+ public:
+ XDragContext(XID local_window,
+ const XClientMessageEvent& event,
+ XDragDropClient* source_client,
+ const SelectionFormatMap& data);
+ ~XDragContext();
+
+ XDragContext(const XDragContext&) = delete;
+ XDragContext& operator=(const XDragContext&) = delete;
+
+ XID source_window() const { return source_window_; }
+ XDragDropClient* source_client() { return source_client_; }
+ const SelectionFormatMap& fetched_targets() const { return fetched_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 OnXdndPositionMessage(XDragDropClient* client,
+ Atom suggested_action,
+ XID source_window,
+ Time time_stamp,
+ const gfx::Point& screen_point);
+
+ // Called when XSelection data has been copied to our process.
+ void OnSelectionNotify(const XSelectionEvent& xselection);
+
+ // Reads the kXdndActionList property from |source_window_| and copies it
+ // into |actions_|.
+ void ReadActions();
+
+ // Creates a DragDropTypes::DragOperation representation of the current
+ // action list.
+ int GetDragOperation() const;
+
+ bool DispatchXEvent(XEvent* event);
+
+ private:
+ // 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();
+
+ // Masks the X11 atom |xdnd_operation|'s views representation onto
+ // |drag_operation|.
+ void MaskOperation(Atom xdnd_operation, int* drag_operation) const;
+
+ // The XID of our chrome local aura window handling our events.
+ XID local_window_;
+
+ // The XID of the window that initiated the drag.
+ XID source_window_;
+
+ // The DesktopDragDropClientAuraX11 for |source_window_| if |source_window_|
+ // belongs to a Chrome window.
+ XDragDropClient* source_client_;
+
+ // The client we inform once we're done with requesting data.
+ XDragDropClient* drag_drop_client_ = nullptr;
+
+ // Whether we're blocking the handling of an XdndPosition message by waiting
+ // for |unfetched_targets_| to be fetched.
+ bool waiting_to_handle_position_ = false;
+
+ // Where the cursor is on screen.
+ gfx::Point screen_point_;
+
+ // The time stamp of the last XdndPosition event we received. The XDND
+ // specification mandates that we use this time stamp when querying the source
+ // about the drag and drop data.
+ Time position_time_stamp_;
+
+ // A SelectionFormatMap of data that we have in our process.
+ 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_;
+
+ // XdndPosition messages have a suggested action. Qt applications exclusively
+ // use this, instead of the XdndActionList which is backed by |actions_|.
+ Atom suggested_action_ = x11::None;
+
+ // Possible actions.
+ std::vector<Atom> actions_;
+};
+
+} // namespace ui
+
+#endif // UI_BASE_X_X11_DRAG_CONTEXT_H_
diff --git a/ui/base/x/x11_drag_drop_client.cc b/ui/base/x/x11_drag_drop_client.cc
new file mode 100644
index 0000000..6f43e4d
--- /dev/null
+++ b/ui/base/x/x11_drag_drop_client.cc
@@ -0,0 +1,71 @@
+// Copyright 2019 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/x11_drag_drop_client.h"
+
+#include "ui/gfx/x/x11_atom_cache.h"
+
+namespace ui {
+
+namespace {
+// These actions have the same meaning as in the W3C Drag and Drop spec.
+const char kXdndActionCopy[] = "XdndActionCopy";
+const char kXdndActionMove[] = "XdndActionMove";
+const char kXdndActionLink[] = "XdndActionLink";
+} // namespace
+
+Atom DragOperationToAtom(int drag_operation) {
+ if (drag_operation & ui::DragDropTypes::DRAG_COPY)
+ return gfx::GetAtom(kXdndActionCopy);
+ if (drag_operation & ui::DragDropTypes::DRAG_MOVE)
+ return gfx::GetAtom(kXdndActionMove);
+ if (drag_operation & ui::DragDropTypes::DRAG_LINK)
+ return gfx::GetAtom(kXdndActionLink);
+
+ return x11::None;
+}
+
+DragDropTypes::DragOperation AtomToDragOperation(Atom atom) {
+ if (atom == gfx::GetAtom(kXdndActionCopy))
+ return DragDropTypes::DRAG_COPY;
+ if (atom == gfx::GetAtom(kXdndActionMove))
+ return DragDropTypes::DRAG_MOVE;
+ if (atom == gfx::GetAtom(kXdndActionLink))
+ return DragDropTypes::DRAG_LINK;
+
+ return DragDropTypes::DRAG_NONE;
+}
+
+XDragDropClient::XDragDropClient(Display* xdisplay, XID xwindow)
+ : xdisplay_(xdisplay), xwindow_(xwindow) {}
+
+XDragDropClient::~XDragDropClient() = default;
+
+XEvent XDragDropClient::PrepareXdndClientMessage(const char* message,
+ XID recipient) const {
+ XEvent xev;
+ xev.xclient.type = ClientMessage;
+ xev.xclient.message_type = gfx::GetAtom(message);
+ xev.xclient.format = 32;
+ xev.xclient.window = recipient;
+ 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;
+ return xev;
+}
+
+std::vector<Atom> XDragDropClient::GetOfferedDragOperations() const {
+ std::vector<Atom> operations;
+ if (drag_operation_ & ui::DragDropTypes::DRAG_COPY)
+ operations.push_back(gfx::GetAtom(kXdndActionCopy));
+ if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE)
+ operations.push_back(gfx::GetAtom(kXdndActionMove));
+ if (drag_operation_ & ui::DragDropTypes::DRAG_LINK)
+ operations.push_back(gfx::GetAtom(kXdndActionLink));
+ return operations;
+}
+
+} // namespace ui
diff --git a/ui/base/x/x11_drag_drop_client.h b/ui/base/x/x11_drag_drop_client.h
new file mode 100644
index 0000000..b10fea5
--- /dev/null
+++ b/ui/base/x/x11_drag_drop_client.h
@@ -0,0 +1,71 @@
+// Copyright 2019 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_X11_DRAG_DROP_CLIENT_H_
+#define UI_BASE_X_X11_DRAG_DROP_CLIENT_H_
+
+#include <vector>
+
+#include "base/component_export.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/x/selection_utils.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/x/x11.h"
+
+namespace ui {
+
+// Converts a bitfield of actions into an Atom that represents what action
+// we're most likely to take on drop.
+COMPONENT_EXPORT(UI_BASE_X) Atom DragOperationToAtom(int drag_operation);
+
+// Converts a single action atom to a drag operation.
+COMPONENT_EXPORT(UI_BASE_X)
+DragDropTypes::DragOperation AtomToDragOperation(Atom atom);
+
+class COMPONENT_EXPORT(UI_BASE_X) XDragDropClient {
+ public:
+ // 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.
+ virtual void CompleteXdndPosition(XID source_window,
+ const gfx::Point& screen_point) = 0;
+
+ int current_modifier_state() const { return current_modifier_state_; }
+
+ // 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() const;
+
+ protected:
+ XDragDropClient(Display* xdisplay, XID xwindow);
+ virtual ~XDragDropClient();
+
+ XDragDropClient(const XDragDropClient&) = delete;
+ XDragDropClient& operator=(const XDragDropClient&) = delete;
+
+ Display* xdisplay() const { return xdisplay_; }
+ XID xwindow() const { return xwindow_; }
+
+ // Creates an XEvent and fills it in with values typical for XDND messages:
+ // the type of event is set to ClientMessage, the format is set to 32 (longs),
+ // and the zero member of data payload is set to |xwindow_|. All other data
+ // members are zeroed, as per XDND specification.
+ XEvent PrepareXdndClientMessage(const char* message, XID recipient) const;
+
+ // The operation bitfield as requested by StartDragAndDrop.
+ int drag_operation_ = 0;
+
+ // The modifier state for the most recent mouse move. Used to bypass an
+ // asynchronous roundtrip through the X11 server.
+ int current_modifier_state_ = 0;
+
+ private:
+ Display* const xdisplay_;
+ const XID xwindow_;
+};
+
+} // namespace ui
+
+#endif // UI_BASE_X_X11_DRAG_DROP_CLIENT_H_
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 d0a6ce71..adae830 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
@@ -24,12 +24,12 @@
#include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
#include "ui/base/layout.h"
#include "ui/base/x/selection_utils.h"
+#include "ui/base/x/x11_drag_context.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/events/platform_event.h"
-#include "ui/events/x/x11_window_event_manager.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_atom_cache.h"
@@ -70,17 +70,9 @@
constexpr int kWillAcceptDrop = 1;
constexpr int kWantFurtherPosEvents = 2;
-// These actions have the same meaning as in the W3C Drag and Drop spec.
-const char kXdndActionCopy[] = "XdndActionCopy";
-const char kXdndActionMove[] = "XdndActionMove";
-const char kXdndActionLink[] = "XdndActionLink";
-
// Triggers the XDS protocol.
const char kXdndActionDirectSave[] = "XdndActionDirectSave";
-// Window property that will receive the drag and drop selection data.
-const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER";
-
// Window property that contains the possible actions that will be presented to
// the user when the drag and drop action is kXdndActionAsk.
const char kXdndActionList[] = "XdndActionList";
@@ -113,9 +105,6 @@
// can be converted to more than 3 types.
const char kXdndTypeList[] = "XdndTypeList";
-// Selection used by the XDND protocol to transfer data between applications.
-const char kXdndSelection[] = "XdndSelection";
-
// Message sent from an XDND source to the target when the user confirms the
// drag and drop operation.
const char kXdndDrop[] = "XdndDrop";
@@ -198,297 +187,23 @@
DesktopDragDropClientAuraX11*
DesktopDragDropClientAuraX11::g_current_drag_drop_client = nullptr;
-class DesktopDragDropClientAuraX11::X11DragContext
- : public ui::PlatformEventDispatcher {
- public:
- X11DragContext(::Window local_window, const XClientMessageEvent& event);
- ~X11DragContext() override;
-
- // 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 OnXdndPositionMessage(DesktopDragDropClientAuraX11* client,
- ::Atom suggested_action,
- ::Window source_window,
- ::Time time_stamp,
- const gfx::Point& screen_point);
-
- // Called when XSelection data has been copied to our process.
- void OnSelectionNotify(const XSelectionEvent& xselection);
-
- // Clones the fetched targets.
- const ui::SelectionFormatMap& fetched_targets() { return fetched_targets_; }
-
- // Reads the kXdndActionList property from |source_window| and copies it
- // into |actions|.
- void ReadActions();
-
- // Creates a ui::DragDropTypes::DragOperation representation of the current
- // action list.
- int GetDragOperation() const;
-
- DesktopDragDropClientAuraX11* source_client() { return source_client_; }
-
- private:
- // 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();
-
- // Masks the X11 atom |xdnd_operation|'s views representation onto
- // |drag_operation|.
- void MaskOperation(::Atom xdnd_operation, int* drag_operation) const;
-
- // ui::PlatformEventDispatcher:
- bool CanDispatchEvent(const ui::PlatformEvent& event) override;
- uint32_t DispatchEvent(const ui::PlatformEvent& event) override;
-
- // 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_;
-
- // Events that we have selected on |source_window_|.
- std::unique_ptr<ui::XScopedEventSelector> source_window_events_;
-
- // The DesktopDragDropClientAuraX11 for |source_window_| if |source_window_|
- // belongs to a Chrome window.
- DesktopDragDropClientAuraX11* source_client_;
-
- // The client we inform once we're done with requesting data.
- DesktopDragDropClientAuraX11* drag_drop_client_ = nullptr;
-
- // Whether we're blocking the handling of an XdndPosition message by waiting
- // for |unfetched_targets_| to be fetched.
- bool waiting_to_handle_position_ = false;
-
- // Where the cursor is on screen.
- gfx::Point screen_point_;
-
- // The time stamp of the last XdndPosition event we received. The XDND
- // specification mandates that we use this time stamp when querying the source
- // about the drag and drop data.
- ::Time position_time_stamp_;
-
- // A SelectionFormatMap of data that we have in our process.
- 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_;
-
- // XdndPosition messages have a suggested action. Qt applications exclusively
- // use this, instead of the XdndActionList which is backed by |actions_|.
- ::Atom suggested_action_ = x11::None;
-
- // Possible actions.
- std::vector<::Atom> actions_;
-
- DISALLOW_COPY_AND_ASSIGN(X11DragContext);
-};
-
-DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
- ::Window local_window,
- const XClientMessageEvent& event)
- : local_window_(local_window),
- source_window_(event.data.l[0]),
- source_client_(
- DesktopDragDropClientAuraX11::GetForWindow(source_window_)) {
- if (!source_client_) {
- bool get_types_from_property = ((event.data.l[1] & 1) != 0);
-
- if (get_types_from_property) {
- if (!ui::GetAtomArrayProperty(source_window_, kXdndTypeList,
- &unfetched_targets_)) {
- return;
- }
- } else {
- // data.l[2,3,4] contain the first three types. Unused slots can be None.
- for (size_t i = 2; i < 5; ++i) {
- if (event.data.l[i] != x11::None)
- unfetched_targets_.push_back(event.data.l[i]);
- }
- }
-
-#if DCHECK_IS_ON()
- DVLOG(1) << "XdndEnter has " << unfetched_targets_.size() << " data types";
- for (::Atom target : unfetched_targets_)
- DVLOG(1) << "XdndEnter data type: " << target;
-#endif // DCHECK_IS_ON()
-
- // The window doesn't have a DesktopDragDropClientAuraX11, that means it's
- // created by some other process. Listen for messages on it.
- ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
- source_window_events_ = std::make_unique<ui::XScopedEventSelector>(
- source_window_, PropertyChangeMask);
-
- // We must perform a full sync here because we could be racing
- // |source_window_|.
- XSync(gfx::GetXDisplay(), x11::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_ = source_client_->GetFormatMap();
- }
-
- ReadActions();
-}
-
-DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() {
- if (!source_client_) {
- // Unsubscribe from message events.
- ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
- }
-}
-
-void DesktopDragDropClientAuraX11::X11DragContext::OnXdndPositionMessage(
- DesktopDragDropClientAuraX11* client,
- ::Atom suggested_action,
- ::Window source_window,
- ::Time time_stamp,
- const gfx::Point& screen_point) {
- DCHECK_EQ(source_window_, source_window);
- suggested_action_ = suggested_action;
-
- 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;
- position_time_stamp_ = time_stamp;
- waiting_to_handle_position_ = true;
-
- fetched_targets_ = ui::SelectionFormatMap();
- RequestNextTarget();
- } else {
- client->CompleteXdndPosition(source_window, screen_point);
- }
-}
-
-void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() {
- DCHECK(!unfetched_targets_.empty());
- DCHECK(drag_drop_client_);
- DCHECK(waiting_to_handle_position_);
-
- ::Atom target = unfetched_targets_.back();
- unfetched_targets_.pop_back();
-
- XConvertSelection(gfx::GetXDisplay(), gfx::GetAtom(kXdndSelection), target,
- gfx::GetAtom(kChromiumDragReciever), local_window_,
- position_time_stamp_);
-}
-
-void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify(
- const XSelectionEvent& event) {
- if (!waiting_to_handle_position_) {
- // A misbehaved window may send SelectionNotify without us requesting data
- // via XConvertSelection().
- return;
- }
- DCHECK(drag_drop_client_);
-
- DVLOG(1) << "SelectionNotify, format " << event.target;
-
- if (event.property != x11::None) {
- DCHECK_EQ(event.property, gfx::GetAtom(kChromiumDragReciever));
-
- scoped_refptr<base::RefCountedMemory> data;
- ::Atom type = x11::None;
- if (ui::GetRawBytesOfProperty(local_window_, event.property, &data, nullptr,
- &type)) {
- fetched_targets_.Insert(event.target, data);
- }
- } else {
- // The source failed to convert the drop data to the format (target in X11
- // parlance) that we asked for. This happens, even though we only ask for
- // the formats advertised by the source. http://crbug.com/628099
- LOG(ERROR) << "XConvertSelection failed for source-advertised target "
- << event.target;
- }
-
- if (!unfetched_targets_.empty()) {
- RequestNextTarget();
- } else {
- waiting_to_handle_position_ = false;
- drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_);
- drag_drop_client_ = nullptr;
- }
-}
-
-void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
- if (!source_client_) {
- std::vector<::Atom> atom_array;
- if (!ui::GetAtomArrayProperty(source_window_, kXdndActionList,
- &atom_array)) {
- actions_.clear();
- } else {
- actions_.swap(atom_array);
- }
- } else {
- // 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_ = source_client_->GetOfferedDragOperations();
- }
-}
-
-int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const {
- int drag_operation = ui::DragDropTypes::DRAG_NONE;
- for (const auto& action : actions_)
- MaskOperation(action, &drag_operation);
-
- MaskOperation(suggested_action_, &drag_operation);
-
- return drag_operation;
-}
-
-void DesktopDragDropClientAuraX11::X11DragContext::MaskOperation(
- ::Atom xdnd_operation,
- int* drag_operation) const {
- if (xdnd_operation == gfx::GetAtom(kXdndActionCopy))
- *drag_operation |= ui::DragDropTypes::DRAG_COPY;
- else if (xdnd_operation == gfx::GetAtom(kXdndActionMove))
- *drag_operation |= ui::DragDropTypes::DRAG_MOVE;
- else if (xdnd_operation == gfx::GetAtom(kXdndActionLink))
- *drag_operation |= ui::DragDropTypes::DRAG_LINK;
-}
-
-bool DesktopDragDropClientAuraX11::X11DragContext::CanDispatchEvent(
- const ui::PlatformEvent& event) {
- return event->xany.window == source_window_;
-}
-
-uint32_t DesktopDragDropClientAuraX11::X11DragContext::DispatchEvent(
- const ui::PlatformEvent& event) {
- if (event->type == PropertyNotify &&
- event->xproperty.atom == gfx::GetAtom(kXdndActionList)) {
- ReadActions();
- return ui::POST_DISPATCH_STOP_PROPAGATION;
- }
- return ui::POST_DISPATCH_NONE;
-}
-
///////////////////////////////////////////////////////////////////////////////
DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
aura::Window* root_window,
views::DesktopNativeCursorManager* cursor_manager,
- ::Display* xdisplay,
- ::Window xwindow)
- : root_window_(root_window),
- cursor_manager_(cursor_manager),
- xdisplay_(xdisplay),
- xwindow_(xwindow) {
+ ::Display* display,
+ XID window)
+ : XDragDropClient(display, window),
+ root_window_(root_window),
+ cursor_manager_(cursor_manager) {
// Some tests change the DesktopDragDropClientAuraX11 associated with an
// |xwindow|.
- g_live_client_map.Get()[xwindow] = this;
+ g_live_client_map.Get()[xwindow()] = this;
// Mark that we are aware of drag and drop concepts.
unsigned long xdnd_version = kMaxXdndVersion;
- XChangeProperty(xdisplay_, xwindow_, gfx::GetAtom(kXdndAware), XA_ATOM, 32,
+ XChangeProperty(xdisplay(), xwindow(), gfx::GetAtom(kXdndAware), XA_ATOM, 32,
PropModeReplace,
reinterpret_cast<unsigned char*>(&xdnd_version), 1);
}
@@ -499,12 +214,14 @@
move_loop_->EndMoveLoop();
NotifyDragLeave();
- g_live_client_map.Get().erase(xwindow_);
+ ResetDragContext();
+
+ g_live_client_map.Get().erase(xwindow());
}
// static
DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow(
- ::Window window) {
+ XID window) {
std::map<::Window, DesktopDragDropClientAuraX11*>::const_iterator it =
g_live_client_map.Get().find(window);
if (it == g_live_client_map.Get().end())
@@ -537,8 +254,19 @@
}
// Make sure that we've run ~X11DragContext() before creating another one.
- target_current_context_.reset();
- target_current_context_ = std::make_unique<X11DragContext>(xwindow_, event);
+ ResetDragContext();
+ XID source_window = event.data.l[0];
+ auto* source_client = GetForWindow(source_window);
+ target_current_context_ = std::make_unique<ui::XDragContext>(
+ xwindow(), event, source_client, source_client->GetFormatMap());
+
+ if (!source_client) {
+ // The window doesn't have a DesktopDragDropClientAuraX11, that means it's
+ // created by some other process. Listen for messages on it.
+ ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
+ source_window_events_ = std::make_unique<ui::XScopedEventSelector>(
+ source_window, PropertyChangeMask);
+ }
// In the Windows implementation, we immediately call DesktopDropTargetWin::
// Translate(). The XDND specification demands that we wait until we receive
@@ -550,14 +278,14 @@
const XClientMessageEvent& event) {
DVLOG(1) << "OnXdndLeave";
NotifyDragLeave();
- target_current_context_.reset();
+ ResetDragContext();
}
void DesktopDragDropClientAuraX11::OnXdndPosition(
const XClientMessageEvent& event) {
DVLOG(1) << "OnXdndPosition";
- unsigned long source_window = event.data.l[0];
+ XID source_window = event.data.l[0];
int x_root_window = event.data.l[2] >> 16;
int y_root_window = event.data.l[2] & 0xffff;
::Time time_stamp = event.data.l[3];
@@ -577,7 +305,7 @@
const XClientMessageEvent& event) {
DVLOG(1) << "OnXdndStatus";
- unsigned long source_window = event.data.l[0];
+ XID source_window = event.data.l[0];
if (source_window != source_current_window_)
return;
@@ -592,7 +320,7 @@
if (event.data.l[1] & 1) {
::Atom atom_operation = event.data.l[4];
- negotiated_operation_ = AtomToDragOperation(atom_operation);
+ negotiated_operation_ = ui::AtomToDragOperation(atom_operation);
} else {
negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
}
@@ -645,7 +373,7 @@
void DesktopDragDropClientAuraX11::OnXdndFinished(
const XClientMessageEvent& event) {
DVLOG(1) << "OnXdndFinished";
- unsigned long source_window = event.data.l[0];
+ XID source_window = event.data.l[0];
if (source_current_window_ != source_window)
return;
@@ -663,7 +391,7 @@
const XClientMessageEvent& event) {
DVLOG(1) << "OnXdndDrop";
- unsigned long source_window = event.data.l[0];
+ XID source_window = event.data.l[0];
int drag_operation = ui::DragDropTypes::DRAG_NONE;
if (target_window_) {
@@ -672,7 +400,7 @@
if (delegate) {
auto data(std::make_unique<ui::OSExchangeData>(
std::make_unique<ui::OSExchangeDataProviderAuraX11>(
- xwindow_, target_current_context_->fetched_targets())));
+ xwindow(), target_current_context_->fetched_targets())));
ui::DropTargetEvent drop_event(
*data.get(), gfx::PointF(target_window_location_),
@@ -696,15 +424,9 @@
target_window_ = nullptr;
}
- XEvent xev;
- xev.xclient.type = ClientMessage;
- xev.xclient.message_type = gfx::GetAtom(kXdndFinished);
- xev.xclient.format = 32;
- xev.xclient.window = source_window;
- xev.xclient.data.l[0] = xwindow_;
+ XEvent xev = PrepareXdndClientMessage(kXdndFinished, source_window);
xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0;
- xev.xclient.data.l[2] = DragOperationToAtom(drag_operation);
-
+ xev.xclient.data.l[2] = ui::DragOperationToAtom(drag_operation);
SendXClientEvent(source_window, &xev);
}
@@ -716,7 +438,7 @@
// ICCCM requires us to delete the property passed into SelectionNotify.
if (xselection.property != x11::None)
- XDeleteProperty(xdisplay_, xwindow_, xselection.property);
+ XDeleteProperty(xdisplay(), xwindow(), xselection.property);
}
int DesktopDragDropClientAuraX11::StartDragAndDrop(
@@ -749,11 +471,11 @@
if (!source_provider_->file_contents_name().empty()) {
actions.push_back(gfx::GetAtom(kXdndActionDirectSave));
ui::SetStringProperty(
- xwindow_, gfx::GetAtom(kXdndDirectSave0),
+ xwindow(), gfx::GetAtom(kXdndDirectSave0),
gfx::GetAtom(ui::kMimeTypeText),
source_provider_->file_contents_name().AsUTF8Unsafe());
}
- ui::SetAtomArrayProperty(xwindow_, kXdndActionList, "ATOM", actions);
+ ui::SetAtomArrayProperty(xwindow(), kXdndActionList, "ATOM", actions);
gfx::ImageSkia drag_image = source_provider_->GetDragImage();
if (IsValidDragImage(drag_image)) {
@@ -792,8 +514,8 @@
source_provider_ = nullptr;
g_current_drag_drop_client = nullptr;
drag_operation_ = 0;
- XDeleteProperty(xdisplay_, xwindow_, gfx::GetAtom(kXdndActionList));
- XDeleteProperty(xdisplay_, xwindow_, gfx::GetAtom(kXdndDirectSave0));
+ XDeleteProperty(xdisplay(), xwindow(), gfx::GetAtom(kXdndActionList));
+ XDeleteProperty(xdisplay(), xwindow(), gfx::GetAtom(kXdndDirectSave0));
return negotiated_operation_;
}
@@ -820,6 +542,21 @@
NOTIMPLEMENTED();
}
+bool DesktopDragDropClientAuraX11::CanDispatchEvent(
+ const ui::PlatformEvent& event) {
+ return target_current_context_.get() &&
+ event->xany.window == target_current_context_->source_window();
+}
+
+uint32_t DesktopDragDropClientAuraX11::DispatchEvent(
+ const ui::PlatformEvent& event) {
+ DCHECK(target_current_context_);
+
+ if (target_current_context_->DispatchXEvent(event))
+ return ui::POST_DISPATCH_STOP_PROPAGATION;
+ return ui::POST_DISPATCH_NONE;
+}
+
void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
DCHECK_EQ(target_window_, window);
target_window_ = nullptr;
@@ -901,7 +638,7 @@
SendXdndLeave(source_current_window_);
source_current_window_ = x11::None;
}
- target_current_context_.reset();
+ ResetDragContext();
repeat_mouse_move_timer_.Stop();
end_move_loop_timer_.Stop();
}
@@ -974,7 +711,7 @@
//
// I'm unsure if I have to jump through those hoops, or if XSendEvent is
// sufficient.
- XSendEvent(xdisplay_, xid, x11::False, 0, xev);
+ XSendEvent(xdisplay(), xid, x11::False, 0, xev);
}
void DesktopDragDropClientAuraX11::ProcessMouseMove(
@@ -1047,7 +784,7 @@
*data = std::make_unique<OSExchangeData>(
std::make_unique<ui::OSExchangeDataProviderAuraX11>(
- xwindow_, target_current_context_->fetched_targets()));
+ xwindow(), target_current_context_->fetched_targets()));
gfx::Point location = root_location;
aura::Window::ConvertPointToTarget(root_window_, target_window_, &location);
@@ -1090,44 +827,9 @@
target_window_ = nullptr;
}
-::Atom DesktopDragDropClientAuraX11::DragOperationToAtom(
- int drag_operation) {
- if (drag_operation & ui::DragDropTypes::DRAG_COPY)
- return gfx::GetAtom(kXdndActionCopy);
- if (drag_operation & ui::DragDropTypes::DRAG_MOVE)
- return gfx::GetAtom(kXdndActionMove);
- if (drag_operation & ui::DragDropTypes::DRAG_LINK)
- return gfx::GetAtom(kXdndActionLink);
-
- return x11::None;
-}
-
-ui::DragDropTypes::DragOperation
-DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
- if (atom == gfx::GetAtom(kXdndActionCopy))
- return ui::DragDropTypes::DRAG_COPY;
- if (atom == gfx::GetAtom(kXdndActionMove))
- return ui::DragDropTypes::DRAG_MOVE;
- if (atom == gfx::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(gfx::GetAtom(kXdndActionCopy));
- if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE)
- operations.push_back(gfx::GetAtom(kXdndActionMove));
- if (drag_operation_ & ui::DragDropTypes::DRAG_LINK)
- operations.push_back(gfx::GetAtom(kXdndActionLink));
- return operations;
-}
-
ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const {
- return source_provider_ ? source_provider_->GetFormatMap() :
- ui::SelectionFormatMap();
+ return source_provider_ ? source_provider_->GetFormatMap()
+ : ui::SelectionFormatMap();
}
void DesktopDragDropClientAuraX11::CompleteXdndPosition(
@@ -1149,39 +851,23 @@
// 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 = gfx::GetAtom(kXdndStatus);
- xev.xclient.format = 32;
- xev.xclient.window = source_window;
- xev.xclient.data.l[0] = xwindow_;
+ XEvent xev = PrepareXdndClientMessage(kXdndStatus, source_window);
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);
-
+ xev.xclient.data.l[4] = ui::DragOperationToAtom(drag_operation);
SendXClientEvent(source_window, &xev);
}
void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) {
- XEvent xev;
- xev.xclient.type = ClientMessage;
- xev.xclient.message_type = gfx::GetAtom(kXdndEnter);
- xev.xclient.format = 32;
- xev.xclient.window = dest_window;
- xev.xclient.data.l[0] = xwindow_;
+ XEvent xev = PrepareXdndClientMessage(kXdndEnter, dest_window);
xev.xclient.data.l[1] = (kMaxXdndVersion << 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_, kXdndTypeList, "ATOM", targets);
+ ui::SetAtomArrayProperty(xwindow(), kXdndTypeList, "ATOM", targets);
} else {
// Pack the targets into the enter message.
for (size_t i = 0; i < targets.size(); ++i)
@@ -1192,16 +878,7 @@
}
void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
- XEvent xev;
- xev.xclient.type = ClientMessage;
- xev.xclient.message_type = gfx::GetAtom(kXdndLeave);
- 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;
+ XEvent xev = PrepareXdndClientMessage(kXdndLeave, dest_window);
SendXClientEvent(dest_window, &xev);
}
@@ -1211,16 +888,10 @@
unsigned long event_time) {
waiting_on_status_ = true;
- XEvent xev;
- xev.xclient.type = ClientMessage;
- xev.xclient.message_type = gfx::GetAtom(kXdndPosition);
- xev.xclient.format = 32;
- xev.xclient.window = dest_window;
- xev.xclient.data.l[0] = xwindow_;
- xev.xclient.data.l[1] = 0;
+ XEvent xev = PrepareXdndClientMessage(kXdndPosition, dest_window);
xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y();
xev.xclient.data.l[3] = event_time;
- xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
+ xev.xclient.data.l[4] = ui::DragOperationToAtom(drag_operation_);
SendXClientEvent(dest_window, &xev);
// http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html and
@@ -1233,16 +904,8 @@
}
void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {
- XEvent xev;
- xev.xclient.type = ClientMessage;
- xev.xclient.message_type = gfx::GetAtom(kXdndDrop);
- xev.xclient.format = 32;
- xev.xclient.window = dest_window;
- xev.xclient.data.l[0] = xwindow_;
- xev.xclient.data.l[1] = 0;
+ XEvent xev = PrepareXdndClientMessage(kXdndDrop, dest_window);
xev.xclient.data.l[2] = x11::CurrentTime;
- xev.xclient.data.l[3] = x11::None;
- xev.xclient.data.l[4] = x11::None;
SendXClientEvent(dest_window, &xev);
}
@@ -1299,4 +962,13 @@
return false;
}
+void DesktopDragDropClientAuraX11::ResetDragContext() {
+ if (!target_current_context_)
+ return;
+ if (!target_current_context_->source_client()) {
+ ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
+ }
+ target_current_context_.reset();
+}
+
} // namespace views
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 f719b1ad..8a00cc93 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
@@ -17,7 +17,10 @@
#include "ui/aura/window_observer.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/x/x11_drag_drop_client.h"
#include "ui/events/event_constants.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/events/x/x11_window_event_manager.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/x/x11.h"
@@ -40,7 +43,7 @@
class DropTargetEvent;
class OSExchangeData;
class OSExchangeDataProviderAuraX11;
-class SelectionFormatMap;
+class XDragContext;
}
namespace views {
@@ -52,7 +55,9 @@
// X11 events forwarded from DesktopWindowTreeHostLinux, while on the other, it
// handles the views drag events.
class VIEWS_EXPORT DesktopDragDropClientAuraX11
- : public aura::client::DragDropClient,
+ : public ui::XDragDropClient,
+ public aura::client::DragDropClient,
+ public ui::PlatformEventDispatcher,
public aura::WindowObserver,
public X11MoveLoopDelegate {
public:
@@ -93,6 +98,10 @@
void AddObserver(aura::client::DragDropClientObserver* observer) override;
void RemoveObserver(aura::client::DragDropClientObserver* observer) override;
+ // PlatformEventDispatcher:
+ bool CanDispatchEvent(const ui::PlatformEvent& event) override;
+ uint32_t DispatchEvent(const ui::PlatformEvent& event) override;
+
// Overridden from aura::WindowObserver:
void OnWindowDestroyed(aura::Window* window) override;
@@ -160,34 +169,16 @@
// longer dragging over it.
void NotifyDragLeave();
- // Converts our bitfield of actions into an Atom that represents what action
- // we're most likely to take on drop.
- ::Atom DragOperationToAtom(int drag_operation);
-
- // Converts a single action atom to a drag operation.
- ui::DragDropTypes::DragOperation 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 representation of the data we're offering in this
// drag. This is done to bypass an asynchronous roundtrip with the X11
// server.
ui::SelectionFormatMap GetFormatMap() const;
- // Returns the modifier state for the most recent mouse move. This is done to
- // bypass an asynchronous roundtrip with the X11 server.
- int current_modifier_state() const {
- return current_modifier_state_;
- }
-
// 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);
+ const gfx::Point& screen_point) override;
void SendXdndEnter(::Window dest_window);
void SendXdndLeave(::Window dest_window);
@@ -203,6 +194,8 @@
// with alpha > 32).
bool IsValidDragImage(const gfx::ImageSkia& image);
+ void ResetDragContext();
+
// A nested run loop that notifies this object of events through the
// X11MoveLoopDelegate interface.
std::unique_ptr<X11MoveLoop> move_loop_;
@@ -211,15 +204,10 @@
DesktopNativeCursorManager* cursor_manager_;
- ::Display* xdisplay_;
- ::Window xwindow_;
-
+ // Events that we have selected on |source_window_|.
+ std::unique_ptr<ui::XScopedEventSelector> source_window_events_;
// Target side information.
- class X11DragContext;
- std::unique_ptr<X11DragContext> target_current_context_;
-
- // The modifier state for the most recent mouse move.
- int current_modifier_state_ = ui::EF_NONE;
+ std::unique_ptr<ui::XDragContext> 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
@@ -262,9 +250,6 @@
// it is important to maintain only one drag and drop operation at any time.
static DesktopDragDropClientAuraX11* g_current_drag_drop_client;
- // The operation bitfield as requested by StartDragAndDrop.
- int drag_operation_ = 0;
-
// We offer the other window a list of possible operations,
// XdndActionsList. This is the requested action from the other window. This
// is DRAG_NONE if we haven't sent out an XdndPosition message yet, haven't