x11_drag_context.cc 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ui/base/x/x11_drag_context.h"
  5. #include "base/logging.h"
  6. #include "base/memory/ref_counted_memory.h"
  7. #include "ui/base/dragdrop/drag_drop_types.h"
  8. #include "ui/base/x/x11_drag_drop_client.h"
  9. #include "ui/base/x/x11_util.h"
  10. #include "ui/events/platform/platform_event_source.h"
  11. #include "ui/gfx/x/connection.h"
  12. #include "ui/gfx/x/x11_atom_cache.h"
  13. #include "ui/gfx/x/xproto.h"
  14. #include "ui/gfx/x/xproto_util.h"
  15. namespace ui {
  16. namespace {
  17. // Window property that holds the supported drag and drop data types.
  18. // This property is set on the XDND source window when the drag and drop data
  19. // can be converted to more than 3 types.
  20. const char kXdndTypeList[] = "XdndTypeList";
  21. // Selection used by the XDND protocol to transfer data between applications.
  22. const char kXdndSelection[] = "XdndSelection";
  23. // Window property that contains the possible actions that will be presented to
  24. // the user when the drag and drop action is kXdndActionAsk.
  25. const char kXdndActionList[] = "XdndActionList";
  26. // These actions have the same meaning as in the W3C Drag and Drop spec.
  27. const char kXdndActionCopy[] = "XdndActionCopy";
  28. const char kXdndActionMove[] = "XdndActionMove";
  29. const char kXdndActionLink[] = "XdndActionLink";
  30. // Window property that will receive the drag and drop selection data.
  31. const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER";
  32. } // namespace
  33. XDragContext::XDragContext(x11::Window local_window,
  34. const x11::ClientMessageEvent& event,
  35. const SelectionFormatMap& data)
  36. : local_window_(local_window),
  37. source_window_(static_cast<x11::Window>(event.data.data32[0])) {
  38. XDragDropClient* source_client =
  39. XDragDropClient::GetForWindow(source_window_);
  40. if (!source_client) {
  41. bool get_types_from_property = ((event.data.data32[1] & 1) != 0);
  42. if (get_types_from_property) {
  43. if (!GetArrayProperty(source_window_, x11::GetAtom(kXdndTypeList),
  44. &unfetched_targets_)) {
  45. return;
  46. }
  47. } else {
  48. // data.l[2,3,4] contain the first three types. Unused slots can be None.
  49. for (size_t i = 2; i < 5; ++i) {
  50. if (event.data.data32[i]) {
  51. unfetched_targets_.push_back(
  52. static_cast<x11::Atom>(event.data.data32[i]));
  53. }
  54. }
  55. }
  56. #if DCHECK_IS_ON()
  57. DVLOG(1) << "XdndEnter has " << unfetched_targets_.size() << " data types";
  58. for (x11::Atom target : unfetched_targets_)
  59. DVLOG(1) << "XdndEnter data type: " << static_cast<uint32_t>(target);
  60. #endif // DCHECK_IS_ON()
  61. // We must perform a full sync here because we could be racing
  62. // |source_window_|.
  63. x11::Connection::Get()->Sync();
  64. } else {
  65. // This drag originates from an aura window within our process. This means
  66. // that we can shortcut the X11 server and ask the owning SelectionOwner
  67. // for the data it's offering.
  68. fetched_targets_ = data;
  69. }
  70. ReadActions();
  71. }
  72. XDragContext::~XDragContext() = default;
  73. void XDragContext::OnXdndPositionMessage(XDragDropClient* client,
  74. x11::Atom suggested_action,
  75. x11::Window source_window,
  76. x11::Time time_stamp,
  77. const gfx::Point& screen_point) {
  78. DCHECK_EQ(source_window_, source_window);
  79. suggested_action_ = suggested_action;
  80. if (!unfetched_targets_.empty()) {
  81. // We have unfetched targets. That means we need to pause the handling of
  82. // the position message and ask the other window for its data.
  83. screen_point_ = screen_point;
  84. drag_drop_client_ = client;
  85. position_time_stamp_ = time_stamp;
  86. waiting_to_handle_position_ = true;
  87. fetched_targets_ = SelectionFormatMap();
  88. RequestNextTarget();
  89. } else {
  90. client->CompleteXdndPosition(source_window, screen_point);
  91. }
  92. }
  93. void XDragContext::RequestNextTarget() {
  94. DCHECK(!unfetched_targets_.empty());
  95. DCHECK(drag_drop_client_);
  96. DCHECK(waiting_to_handle_position_);
  97. x11::Atom target = unfetched_targets_.back();
  98. unfetched_targets_.pop_back();
  99. x11::Connection::Get()->ConvertSelection(
  100. {local_window_, x11::GetAtom(kXdndSelection), target,
  101. x11::GetAtom(kChromiumDragReciever), position_time_stamp_});
  102. }
  103. void XDragContext::OnSelectionNotify(const x11::SelectionNotifyEvent& event) {
  104. if (!waiting_to_handle_position_) {
  105. // A misbehaved window may send SelectionNotify without us requesting data
  106. // via XConvertSelection().
  107. return;
  108. }
  109. DCHECK(drag_drop_client_);
  110. DVLOG(1) << "SelectionNotify, format " << static_cast<uint32_t>(event.target);
  111. auto property = static_cast<x11::Atom>(event.property);
  112. auto target = static_cast<x11::Atom>(event.target);
  113. if (event.property != x11::Atom::None) {
  114. DCHECK_EQ(property, x11::GetAtom(kChromiumDragReciever));
  115. scoped_refptr<base::RefCountedMemory> data;
  116. x11::Atom type = x11::Atom::None;
  117. if (GetRawBytesOfProperty(local_window_, property, &data, &type))
  118. fetched_targets_.Insert(target, data);
  119. } else {
  120. // The source failed to convert the drop data to the format (target in X11
  121. // parlance) that we asked for. This happens, even though we only ask for
  122. // the formats advertised by the source. http://crbug.com/628099
  123. LOG(ERROR) << "XConvertSelection failed for source-advertised target "
  124. << static_cast<uint32_t>(event.target);
  125. }
  126. if (!unfetched_targets_.empty()) {
  127. RequestNextTarget();
  128. } else {
  129. waiting_to_handle_position_ = false;
  130. drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_);
  131. drag_drop_client_ = nullptr;
  132. }
  133. }
  134. void XDragContext::ReadActions() {
  135. XDragDropClient* source_client =
  136. XDragDropClient::GetForWindow(source_window_);
  137. if (!source_client) {
  138. std::vector<x11::Atom> atom_array;
  139. if (!GetArrayProperty(source_window_, x11::GetAtom(kXdndActionList),
  140. &atom_array)) {
  141. actions_.clear();
  142. } else {
  143. actions_.swap(atom_array);
  144. }
  145. } else {
  146. // We have a property notify set up for other windows in case they change
  147. // their action list. Thankfully, the views interface is static and you
  148. // can't change the action list after you enter StartDragAndDrop().
  149. actions_ = source_client->GetOfferedDragOperations();
  150. }
  151. }
  152. int XDragContext::GetDragOperation() const {
  153. int drag_operation = DragDropTypes::DRAG_NONE;
  154. for (const auto& action : actions_)
  155. MaskOperation(action, &drag_operation);
  156. MaskOperation(suggested_action_, &drag_operation);
  157. return drag_operation;
  158. }
  159. void XDragContext::MaskOperation(x11::Atom xdnd_operation,
  160. int* drag_operation) const {
  161. if (xdnd_operation == x11::GetAtom(kXdndActionCopy))
  162. *drag_operation |= DragDropTypes::DRAG_COPY;
  163. else if (xdnd_operation == x11::GetAtom(kXdndActionMove))
  164. *drag_operation |= DragDropTypes::DRAG_MOVE;
  165. else if (xdnd_operation == x11::GetAtom(kXdndActionLink))
  166. *drag_operation |= DragDropTypes::DRAG_LINK;
  167. }
  168. bool XDragContext::DispatchPropertyNotifyEvent(
  169. const x11::PropertyNotifyEvent& prop) {
  170. if (prop.atom == x11::GetAtom(kXdndActionList)) {
  171. ReadActions();
  172. return true;
  173. }
  174. return false;
  175. }
  176. } // namespace ui