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