123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- // 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 "base/logging.h"
- #include "base/memory/ref_counted_memory.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/connection.h"
- #include "ui/gfx/x/x11_atom_cache.h"
- #include "ui/gfx/x/xproto.h"
- #include "ui/gfx/x/xproto_util.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(x11::Window local_window,
- const x11::ClientMessageEvent& event,
- const SelectionFormatMap& data)
- : local_window_(local_window),
- source_window_(static_cast<x11::Window>(event.data.data32[0])) {
- XDragDropClient* source_client =
- XDragDropClient::GetForWindow(source_window_);
- if (!source_client) {
- bool get_types_from_property = ((event.data.data32[1] & 1) != 0);
- if (get_types_from_property) {
- if (!GetArrayProperty(source_window_, x11::GetAtom(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.data32[i]) {
- unfetched_targets_.push_back(
- static_cast<x11::Atom>(event.data.data32[i]));
- }
- }
- }
- #if DCHECK_IS_ON()
- DVLOG(1) << "XdndEnter has " << unfetched_targets_.size() << " data types";
- for (x11::Atom target : unfetched_targets_)
- DVLOG(1) << "XdndEnter data type: " << static_cast<uint32_t>(target);
- #endif // DCHECK_IS_ON()
- // We must perform a full sync here because we could be racing
- // |source_window_|.
- x11::Connection::Get()->Sync();
- } 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,
- x11::Atom suggested_action,
- x11::Window source_window,
- x11::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_);
- x11::Atom target = unfetched_targets_.back();
- unfetched_targets_.pop_back();
- x11::Connection::Get()->ConvertSelection(
- {local_window_, x11::GetAtom(kXdndSelection), target,
- x11::GetAtom(kChromiumDragReciever), position_time_stamp_});
- }
- void XDragContext::OnSelectionNotify(const x11::SelectionNotifyEvent& 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 " << static_cast<uint32_t>(event.target);
- auto property = static_cast<x11::Atom>(event.property);
- auto target = static_cast<x11::Atom>(event.target);
- if (event.property != x11::Atom::None) {
- DCHECK_EQ(property, x11::GetAtom(kChromiumDragReciever));
- scoped_refptr<base::RefCountedMemory> data;
- x11::Atom type = x11::Atom::None;
- if (GetRawBytesOfProperty(local_window_, property, &data, &type))
- fetched_targets_.Insert(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 "
- << static_cast<uint32_t>(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() {
- XDragDropClient* source_client =
- XDragDropClient::GetForWindow(source_window_);
- if (!source_client) {
- std::vector<x11::Atom> atom_array;
- if (!GetArrayProperty(source_window_, x11::GetAtom(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(x11::Atom xdnd_operation,
- int* drag_operation) const {
- if (xdnd_operation == x11::GetAtom(kXdndActionCopy))
- *drag_operation |= DragDropTypes::DRAG_COPY;
- else if (xdnd_operation == x11::GetAtom(kXdndActionMove))
- *drag_operation |= DragDropTypes::DRAG_MOVE;
- else if (xdnd_operation == x11::GetAtom(kXdndActionLink))
- *drag_operation |= DragDropTypes::DRAG_LINK;
- }
- bool XDragContext::DispatchPropertyNotifyEvent(
- const x11::PropertyNotifyEvent& prop) {
- if (prop.atom == x11::GetAtom(kXdndActionList)) {
- ReadActions();
- return true;
- }
- return false;
- }
- } // namespace ui
|