data_device.cc 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. // Copyright 2017 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 "components/exo/data_device.h"
  5. #include "base/bind.h"
  6. #include "base/callback_helpers.h"
  7. #include "base/run_loop.h"
  8. #include "build/chromeos_buildflags.h"
  9. #include "components/exo/data_device_delegate.h"
  10. #include "components/exo/data_exchange_delegate.h"
  11. #include "components/exo/data_offer.h"
  12. #include "components/exo/data_source.h"
  13. #include "components/exo/seat.h"
  14. #include "components/exo/shell_surface_util.h"
  15. #include "components/exo/surface.h"
  16. #include "ui/aura/client/drag_drop_delegate.h"
  17. #include "ui/base/clipboard/clipboard.h"
  18. #include "ui/base/clipboard/clipboard_monitor.h"
  19. #include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
  20. #include "ui/base/dragdrop/drag_drop_types.h"
  21. #include "ui/base/dragdrop/drop_target_event.h"
  22. #include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
  23. #if BUILDFLAG(IS_CHROMEOS_ASH)
  24. #include "chromeos/ui/base/window_properties.h"
  25. #include "components/exo/extended_drag_source.h"
  26. #endif
  27. namespace exo {
  28. namespace {
  29. using ::ui::mojom::DragOperation;
  30. constexpr int kDataDeviceSeatObserverPriority = 0;
  31. static_assert(Seat::IsValidObserverPriority(kDataDeviceSeatObserverPriority),
  32. "kDataDeviceSeatObserverPriority is not in the valid range.");
  33. constexpr base::TimeDelta kDataOfferDestructionTimeout =
  34. base::Milliseconds(1000);
  35. DragOperation DndActionToDragOperation(DndAction dnd_action) {
  36. switch (dnd_action) {
  37. case DndAction::kMove:
  38. return DragOperation::kMove;
  39. case DndAction::kCopy:
  40. return DragOperation::kCopy;
  41. case DndAction::kAsk:
  42. return DragOperation::kLink;
  43. case DndAction::kNone:
  44. return DragOperation::kNone;
  45. }
  46. }
  47. } // namespace
  48. DataDevice::DataDevice(DataDeviceDelegate* delegate, Seat* seat)
  49. : delegate_(delegate), seat_(seat), drop_succeeded_(false) {
  50. WMHelper::GetInstance()->AddDragDropObserver(this);
  51. ui::ClipboardMonitor::GetInstance()->AddObserver(this);
  52. seat_->AddObserver(this, kDataDeviceSeatObserverPriority);
  53. OnSurfaceFocused(seat_->GetFocusedSurface(), nullptr,
  54. !!seat_->GetFocusedSurface());
  55. }
  56. DataDevice::~DataDevice() {
  57. delegate_->OnDataDeviceDestroying(this);
  58. WMHelper::GetInstance()->RemoveDragDropObserver(this);
  59. ui::ClipboardMonitor::GetInstance()->RemoveObserver(this);
  60. seat_->RemoveObserver(this);
  61. }
  62. void DataDevice::StartDrag(DataSource* source,
  63. Surface* origin,
  64. Surface* icon,
  65. ui::mojom::DragEventSource event_source) {
  66. seat_->StartDrag(source, origin, icon, event_source);
  67. }
  68. void DataDevice::SetSelection(DataSource* source) {
  69. seat_->SetSelection(source);
  70. }
  71. void DataDevice::OnDragEntered(const ui::DropTargetEvent& event) {
  72. DCHECK(!data_offer_);
  73. Surface* surface = GetEffectiveTargetForEvent(event);
  74. if (!surface)
  75. return;
  76. base::flat_set<DndAction> dnd_actions;
  77. if (event.source_operations() & ui::DragDropTypes::DRAG_MOVE) {
  78. dnd_actions.insert(DndAction::kMove);
  79. }
  80. if (event.source_operations() & ui::DragDropTypes::DRAG_COPY) {
  81. dnd_actions.insert(DndAction::kCopy);
  82. }
  83. if (event.source_operations() & ui::DragDropTypes::DRAG_LINK) {
  84. dnd_actions.insert(DndAction::kAsk);
  85. }
  86. data_offer_ =
  87. std::make_unique<ScopedDataOffer>(delegate_->OnDataOffer(), this);
  88. data_offer_->get()->SetDropData(seat_->data_exchange_delegate(),
  89. surface->window(), event.data());
  90. data_offer_->get()->SetSourceActions(dnd_actions);
  91. data_offer_->get()->SetActions(base::flat_set<DndAction>(), DndAction::kAsk);
  92. delegate_->OnEnter(surface, event.location_f(), *data_offer_->get());
  93. }
  94. aura::client::DragUpdateInfo DataDevice::OnDragUpdated(
  95. const ui::DropTargetEvent& event) {
  96. if (!data_offer_)
  97. return aura::client::DragUpdateInfo();
  98. ui::EndpointType endpoint_type = ui::EndpointType::kDefault;
  99. Surface* surface = GetEffectiveTargetForEvent(event);
  100. if (surface) {
  101. endpoint_type =
  102. seat_->data_exchange_delegate()->GetDataTransferEndpointType(
  103. surface->window());
  104. }
  105. aura::client::DragUpdateInfo drag_info(
  106. ui::DragDropTypes::DRAG_NONE, ui::DataTransferEndpoint(endpoint_type));
  107. bool prevent_motion_drag_events = false;
  108. #if BUILDFLAG(IS_CHROMEOS_ASH)
  109. // chromeos::kCanAttachToAnotherWindowKey controls if a drag operation should
  110. // trigger swallow/unswallow tab.
  111. if (focused_surface_) {
  112. // The ExtendedDragSource instance can be null for tests.
  113. auto* extended_drag_source = ExtendedDragSource::Get();
  114. bool is_extended_drag_source_active =
  115. extended_drag_source && extended_drag_source->IsActive();
  116. prevent_motion_drag_events =
  117. is_extended_drag_source_active &&
  118. !focused_surface_->get()->window()->GetToplevelWindow()->GetProperty(
  119. chromeos::kCanAttachToAnotherWindowKey);
  120. }
  121. #endif
  122. if (!prevent_motion_drag_events)
  123. delegate_->OnMotion(event.time_stamp(), event.location_f());
  124. // TODO(hirono): dnd_action() here may not be updated. Chrome needs to provide
  125. // a way to update DND action asynchronously.
  126. drag_info.drag_operation = static_cast<int>(
  127. DndActionToDragOperation(data_offer_->get()->dnd_action()));
  128. return drag_info;
  129. }
  130. void DataDevice::OnDragExited() {
  131. if (!data_offer_)
  132. return;
  133. delegate_->OnLeave();
  134. data_offer_.reset();
  135. }
  136. WMHelper::DragDropObserver::DropCallback DataDevice::GetDropCallback() {
  137. base::ScopedClosureRunner drag_exit(
  138. base::BindOnce(&DataDevice::OnDragExited, weak_factory_.GetWeakPtr()));
  139. return base::BindOnce(&DataDevice::PerformDropOrExitDrag,
  140. drop_weak_factory_.GetWeakPtr(), std::move(drag_exit));
  141. }
  142. void DataDevice::OnClipboardDataChanged() {
  143. if (!focused_surface_)
  144. return;
  145. SetSelectionToCurrentClipboardData();
  146. }
  147. void DataDevice::OnSurfaceFocused(Surface* gained_surface,
  148. Surface* lost_focused,
  149. bool has_focused_surface) {
  150. Surface* next_focused_surface =
  151. gained_surface && delegate_->CanAcceptDataEventsForSurface(gained_surface)
  152. ? gained_surface
  153. : nullptr;
  154. // Check if focused surface is not changed.
  155. if (focused_surface_ && focused_surface_->get() == next_focused_surface)
  156. return;
  157. std::unique_ptr<ScopedSurface> last_focused_surface =
  158. std::move(focused_surface_);
  159. focused_surface_ = next_focused_surface ? std::make_unique<ScopedSurface>(
  160. next_focused_surface, this)
  161. : nullptr;
  162. // Check if the client newly obtained focus.
  163. if (focused_surface_ && !last_focused_surface)
  164. SetSelectionToCurrentClipboardData();
  165. }
  166. void DataDevice::OnDataOfferDestroying(DataOffer* data_offer) {
  167. if (data_offer_ && data_offer_->get() == data_offer) {
  168. drop_succeeded_ = data_offer_->get()->finished();
  169. if (quit_closure_)
  170. std::move(quit_closure_).Run();
  171. data_offer_.reset();
  172. }
  173. drop_weak_factory_.InvalidateWeakPtrs();
  174. }
  175. void DataDevice::OnSurfaceDestroying(Surface* surface) {
  176. if (focused_surface_ && focused_surface_->get() == surface)
  177. focused_surface_.reset();
  178. }
  179. Surface* DataDevice::GetEffectiveTargetForEvent(
  180. const ui::DropTargetEvent& event) const {
  181. aura::Window* window = static_cast<aura::Window*>(event.target());
  182. if (!window)
  183. return nullptr;
  184. Surface* target = Surface::AsSurface(window);
  185. if (!target)
  186. return nullptr;
  187. return delegate_->CanAcceptDataEventsForSurface(target) ? target : nullptr;
  188. }
  189. void DataDevice::SetSelectionToCurrentClipboardData() {
  190. DCHECK(focused_surface_);
  191. DataOffer* data_offer = delegate_->OnDataOffer();
  192. data_offer->SetClipboardData(
  193. seat_->data_exchange_delegate(), *ui::Clipboard::GetForCurrentThread(),
  194. seat_->data_exchange_delegate()->GetDataTransferEndpointType(
  195. focused_surface_->get()->window()));
  196. delegate_->OnSelection(*data_offer);
  197. }
  198. void DataDevice::PerformDropOrExitDrag(
  199. base::ScopedClosureRunner exit_drag,
  200. ui::mojom::DragOperation& output_drag_op) {
  201. exit_drag.ReplaceClosure(base::DoNothing());
  202. if (!data_offer_) {
  203. output_drag_op = DragOperation::kNone;
  204. return;
  205. }
  206. DndAction dnd_action = data_offer_->get()->dnd_action();
  207. delegate_->OnDrop();
  208. // TODO(crbug.com/1160925): Avoid using nested loop by adding asynchronous
  209. // callback to aura::client::DragDropDelegate.
  210. base::WeakPtr<DataDevice> alive(weak_factory_.GetWeakPtr());
  211. base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
  212. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  213. FROM_HERE, run_loop.QuitClosure(), kDataOfferDestructionTimeout);
  214. quit_closure_ = run_loop.QuitClosure();
  215. run_loop.Run();
  216. if (!alive) {
  217. output_drag_op = DragOperation::kNone;
  218. return;
  219. }
  220. if (quit_closure_) {
  221. // DataOffer not destroyed by the client until the timeout.
  222. quit_closure_.Reset();
  223. data_offer_.reset();
  224. drop_succeeded_ = false;
  225. }
  226. if (!drop_succeeded_)
  227. output_drag_op = DragOperation::kNone;
  228. else
  229. output_drag_op = DndActionToDragOperation(dnd_action);
  230. }
  231. } // namespace exo