1 // Copyright 2018 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
5 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/run_loop.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/thread_task_runner_handle.h"
15 #include "ui/aura/client/capture_client.h"
16 #include "ui/aura/client/cursor_client.h"
17 #include "ui/aura/client/drag_drop_delegate.h"
18 #include "ui/aura/window.h"
19 #include "ui/aura/window_tree_host.h"
20 #include "ui/base/clipboard/clipboard.h"
21 #include "ui/base/dragdrop/drag_drop_types.h"
22 #include "ui/base/dragdrop/drop_target_event.h"
23 #include "ui/base/dragdrop/os_exchange_data_provider_aura.h"
24 #include "ui/base/mojom/cursor_type.mojom-shared.h"
25 #include "ui/platform_window/platform_window_delegate.h"
26 #include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
27 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
28
29 namespace views {
30
31 namespace {
32
GetTargetWindow(aura::Window * root_window,const gfx::Point & point)33 aura::Window* GetTargetWindow(aura::Window* root_window,
34 const gfx::Point& point) {
35 gfx::Point root_location(point);
36 root_window->GetHost()->ConvertScreenInPixelsToDIP(&root_location);
37 return root_window->GetEventHandlerForPoint(root_location);
38 }
39
40 } // namespace
41
DesktopDragDropClientOzone(aura::Window * root_window,views::DesktopNativeCursorManager * cursor_manager,ui::WmDragHandler * drag_handler)42 DesktopDragDropClientOzone::DesktopDragDropClientOzone(
43 aura::Window* root_window,
44 views::DesktopNativeCursorManager* cursor_manager,
45 ui::WmDragHandler* drag_handler)
46 : root_window_(root_window),
47 cursor_manager_(cursor_manager),
48 drag_handler_(drag_handler) {}
49
~DesktopDragDropClientOzone()50 DesktopDragDropClientOzone::~DesktopDragDropClientOzone() {
51 ResetDragDropTarget();
52
53 if (in_move_loop_)
54 DragCancel();
55 }
56
StartDragAndDrop(std::unique_ptr<ui::OSExchangeData> data,aura::Window * root_window,aura::Window * source_window,const gfx::Point & root_location,int operation,ui::DragDropTypes::DragEventSource source)57 int DesktopDragDropClientOzone::StartDragAndDrop(
58 std::unique_ptr<ui::OSExchangeData> data,
59 aura::Window* root_window,
60 aura::Window* source_window,
61 const gfx::Point& root_location,
62 int operation,
63 ui::DragDropTypes::DragEventSource source) {
64 if (!drag_handler_)
65 return ui::DragDropTypes::DragOperation::DRAG_NONE;
66
67 DCHECK(!in_move_loop_);
68 base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
69 quit_closure_ = run_loop.QuitClosure();
70
71 // Chrome expects starting drag and drop to release capture.
72 aura::Window* capture_window =
73 aura::client::GetCaptureClient(root_window)->GetGlobalCaptureWindow();
74 if (capture_window)
75 capture_window->ReleaseCapture();
76
77 aura::client::CursorClient* cursor_client =
78 aura::client::GetCursorClient(root_window);
79
80 initial_cursor_ = source_window->GetHost()->last_cursor();
81 drag_operation_ = operation;
82 cursor_client->SetCursor(
83 cursor_manager_->GetInitializedCursor(ui::mojom::CursorType::kGrabbing));
84
85 drag_handler_->StartDrag(
86 *data.get(), operation, cursor_client->GetCursor(),
87 base::BindOnce(&DesktopDragDropClientOzone::OnDragSessionClosed,
88 base::Unretained(this)));
89 in_move_loop_ = true;
90 run_loop.Run();
91 DragDropSessionCompleted();
92 return drag_operation_;
93 }
94
DragCancel()95 void DesktopDragDropClientOzone::DragCancel() {
96 QuitRunLoop();
97 }
98
IsDragDropInProgress()99 bool DesktopDragDropClientOzone::IsDragDropInProgress() {
100 return in_move_loop_;
101 }
102
AddObserver(aura::client::DragDropClientObserver * observer)103 void DesktopDragDropClientOzone::AddObserver(
104 aura::client::DragDropClientObserver* observer) {
105 NOTIMPLEMENTED_LOG_ONCE();
106 }
107
RemoveObserver(aura::client::DragDropClientObserver * observer)108 void DesktopDragDropClientOzone::RemoveObserver(
109 aura::client::DragDropClientObserver* observer) {
110 NOTIMPLEMENTED_LOG_ONCE();
111 }
112
OnDragEnter(const gfx::PointF & point,std::unique_ptr<ui::OSExchangeData> data,int operation)113 void DesktopDragDropClientOzone::OnDragEnter(
114 const gfx::PointF& point,
115 std::unique_ptr<ui::OSExchangeData> data,
116 int operation) {
117 last_drag_point_ = point;
118 drag_operation_ = operation;
119
120 // If it doesn't have |data|, it defers sending events to
121 // |drag_drop_delegate_|. It will try again before handling drop.
122 if (!data)
123 return;
124
125 os_exchange_data_ = std::move(data);
126 std::unique_ptr<ui::DropTargetEvent> event = CreateDropTargetEvent(point);
127 if (drag_drop_delegate_ && event)
128 drag_drop_delegate_->OnDragEntered(*event);
129 }
130
OnDragMotion(const gfx::PointF & point,int operation)131 int DesktopDragDropClientOzone::OnDragMotion(const gfx::PointF& point,
132 int operation) {
133 last_drag_point_ = point;
134 drag_operation_ = operation;
135 int client_operation =
136 ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE;
137
138 if (os_exchange_data_) {
139 std::unique_ptr<ui::DropTargetEvent> event = CreateDropTargetEvent(point);
140 // If |os_exchange_data_| has a valid data, |drag_drop_delegate_| returns
141 // the operation which it expects.
142 if (drag_drop_delegate_ && event)
143 client_operation = drag_drop_delegate_->OnDragUpdated(*event);
144 }
145 return client_operation;
146 }
147
OnDragDrop(std::unique_ptr<ui::OSExchangeData> data)148 void DesktopDragDropClientOzone::OnDragDrop(
149 std::unique_ptr<ui::OSExchangeData> data) {
150 // If it doesn't have |os_exchange_data_|, it needs to update it with |data|.
151 if (!os_exchange_data_) {
152 DCHECK(data);
153 os_exchange_data_ = std::move(data);
154 std::unique_ptr<ui::DropTargetEvent> event =
155 CreateDropTargetEvent(last_drag_point_);
156 // Sends the deferred drag events to |drag_drop_delegate_| before handling
157 // drop.
158 if (drag_drop_delegate_ && event) {
159 drag_drop_delegate_->OnDragEntered(*event);
160 // TODO(jkim): It doesn't use the return value from 'OnDragUpdated' and
161 // doesn't have a chance to update the expected operation.
162 // https://crbug.com/875164
163 drag_drop_delegate_->OnDragUpdated(*event);
164 }
165 } else {
166 // If it has |os_exchange_data_|, it doesn't expect |data| on OnDragDrop.
167 DCHECK(!data);
168 }
169 PerformDrop();
170 }
171
OnDragLeave()172 void DesktopDragDropClientOzone::OnDragLeave() {
173 os_exchange_data_.reset();
174 ResetDragDropTarget();
175 }
176
OnDragSessionClosed(int dnd_action)177 void DesktopDragDropClientOzone::OnDragSessionClosed(int dnd_action) {
178 drag_operation_ = dnd_action;
179 QuitRunLoop();
180 }
181
DragDropSessionCompleted()182 void DesktopDragDropClientOzone::DragDropSessionCompleted() {
183 aura::client::CursorClient* cursor_client =
184 aura::client::GetCursorClient(root_window_);
185 if (!cursor_client)
186 return;
187
188 cursor_client->SetCursor(initial_cursor_);
189 }
190
QuitRunLoop()191 void DesktopDragDropClientOzone::QuitRunLoop() {
192 in_move_loop_ = false;
193 if (quit_closure_.is_null())
194 return;
195 std::move(quit_closure_).Run();
196 }
197
198 std::unique_ptr<ui::DropTargetEvent>
CreateDropTargetEvent(const gfx::PointF & location)199 DesktopDragDropClientOzone::CreateDropTargetEvent(const gfx::PointF& location) {
200 const gfx::Point point(location.x(), location.y());
201 aura::Window* window = GetTargetWindow(root_window_, point);
202 if (!window)
203 return nullptr;
204
205 UpdateDragDropDelegate(window);
206 gfx::Point root_location(location.x(), location.y());
207 root_window_->GetHost()->ConvertScreenInPixelsToDIP(&root_location);
208 gfx::PointF target_location(root_location);
209 aura::Window::ConvertPointToTarget(root_window_, window, &target_location);
210
211 return std::make_unique<ui::DropTargetEvent>(
212 *os_exchange_data_, target_location, gfx::PointF(root_location),
213 drag_operation_);
214 }
215
UpdateDragDropDelegate(aura::Window * window)216 void DesktopDragDropClientOzone::UpdateDragDropDelegate(aura::Window* window) {
217 aura::client::DragDropDelegate* delegate =
218 aura::client::GetDragDropDelegate(window);
219
220 if (drag_drop_delegate_ == delegate)
221 return;
222
223 ResetDragDropTarget();
224 if (delegate)
225 drag_drop_delegate_ = delegate;
226 }
227
ResetDragDropTarget()228 void DesktopDragDropClientOzone::ResetDragDropTarget() {
229 if (!drag_drop_delegate_)
230 return;
231 drag_drop_delegate_->OnDragExited();
232 drag_drop_delegate_ = nullptr;
233 }
234
PerformDrop()235 void DesktopDragDropClientOzone::PerformDrop() {
236 std::unique_ptr<ui::DropTargetEvent> event =
237 CreateDropTargetEvent(last_drag_point_);
238 if (drag_drop_delegate_ && event)
239 drag_operation_ = drag_drop_delegate_->OnPerformDrop(
240 *event, std::move(os_exchange_data_));
241 DragDropSessionCompleted();
242 }
243
244 } // namespace views
245