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