123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- // Copyright 2017 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 "components/exo/data_device.h"
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/run_loop.h"
- #include "build/chromeos_buildflags.h"
- #include "components/exo/data_device_delegate.h"
- #include "components/exo/data_exchange_delegate.h"
- #include "components/exo/data_offer.h"
- #include "components/exo/data_source.h"
- #include "components/exo/seat.h"
- #include "components/exo/shell_surface_util.h"
- #include "components/exo/surface.h"
- #include "ui/aura/client/drag_drop_delegate.h"
- #include "ui/base/clipboard/clipboard.h"
- #include "ui/base/clipboard/clipboard_monitor.h"
- #include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
- #include "ui/base/dragdrop/drag_drop_types.h"
- #include "ui/base/dragdrop/drop_target_event.h"
- #include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- #include "chromeos/ui/base/window_properties.h"
- #include "components/exo/extended_drag_source.h"
- #endif
- namespace exo {
- namespace {
- using ::ui::mojom::DragOperation;
- constexpr int kDataDeviceSeatObserverPriority = 0;
- static_assert(Seat::IsValidObserverPriority(kDataDeviceSeatObserverPriority),
- "kDataDeviceSeatObserverPriority is not in the valid range.");
- constexpr base::TimeDelta kDataOfferDestructionTimeout =
- base::Milliseconds(1000);
- DragOperation DndActionToDragOperation(DndAction dnd_action) {
- switch (dnd_action) {
- case DndAction::kMove:
- return DragOperation::kMove;
- case DndAction::kCopy:
- return DragOperation::kCopy;
- case DndAction::kAsk:
- return DragOperation::kLink;
- case DndAction::kNone:
- return DragOperation::kNone;
- }
- }
- } // namespace
- DataDevice::DataDevice(DataDeviceDelegate* delegate, Seat* seat)
- : delegate_(delegate), seat_(seat), drop_succeeded_(false) {
- WMHelper::GetInstance()->AddDragDropObserver(this);
- ui::ClipboardMonitor::GetInstance()->AddObserver(this);
- seat_->AddObserver(this, kDataDeviceSeatObserverPriority);
- OnSurfaceFocused(seat_->GetFocusedSurface(), nullptr,
- !!seat_->GetFocusedSurface());
- }
- DataDevice::~DataDevice() {
- delegate_->OnDataDeviceDestroying(this);
- WMHelper::GetInstance()->RemoveDragDropObserver(this);
- ui::ClipboardMonitor::GetInstance()->RemoveObserver(this);
- seat_->RemoveObserver(this);
- }
- void DataDevice::StartDrag(DataSource* source,
- Surface* origin,
- Surface* icon,
- ui::mojom::DragEventSource event_source) {
- seat_->StartDrag(source, origin, icon, event_source);
- }
- void DataDevice::SetSelection(DataSource* source) {
- seat_->SetSelection(source);
- }
- void DataDevice::OnDragEntered(const ui::DropTargetEvent& event) {
- DCHECK(!data_offer_);
- Surface* surface = GetEffectiveTargetForEvent(event);
- if (!surface)
- return;
- base::flat_set<DndAction> dnd_actions;
- if (event.source_operations() & ui::DragDropTypes::DRAG_MOVE) {
- dnd_actions.insert(DndAction::kMove);
- }
- if (event.source_operations() & ui::DragDropTypes::DRAG_COPY) {
- dnd_actions.insert(DndAction::kCopy);
- }
- if (event.source_operations() & ui::DragDropTypes::DRAG_LINK) {
- dnd_actions.insert(DndAction::kAsk);
- }
- data_offer_ =
- std::make_unique<ScopedDataOffer>(delegate_->OnDataOffer(), this);
- data_offer_->get()->SetDropData(seat_->data_exchange_delegate(),
- surface->window(), event.data());
- data_offer_->get()->SetSourceActions(dnd_actions);
- data_offer_->get()->SetActions(base::flat_set<DndAction>(), DndAction::kAsk);
- delegate_->OnEnter(surface, event.location_f(), *data_offer_->get());
- }
- aura::client::DragUpdateInfo DataDevice::OnDragUpdated(
- const ui::DropTargetEvent& event) {
- if (!data_offer_)
- return aura::client::DragUpdateInfo();
- ui::EndpointType endpoint_type = ui::EndpointType::kDefault;
- Surface* surface = GetEffectiveTargetForEvent(event);
- if (surface) {
- endpoint_type =
- seat_->data_exchange_delegate()->GetDataTransferEndpointType(
- surface->window());
- }
- aura::client::DragUpdateInfo drag_info(
- ui::DragDropTypes::DRAG_NONE, ui::DataTransferEndpoint(endpoint_type));
- bool prevent_motion_drag_events = false;
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- // chromeos::kCanAttachToAnotherWindowKey controls if a drag operation should
- // trigger swallow/unswallow tab.
- if (focused_surface_) {
- // The ExtendedDragSource instance can be null for tests.
- auto* extended_drag_source = ExtendedDragSource::Get();
- bool is_extended_drag_source_active =
- extended_drag_source && extended_drag_source->IsActive();
- prevent_motion_drag_events =
- is_extended_drag_source_active &&
- !focused_surface_->get()->window()->GetToplevelWindow()->GetProperty(
- chromeos::kCanAttachToAnotherWindowKey);
- }
- #endif
- if (!prevent_motion_drag_events)
- delegate_->OnMotion(event.time_stamp(), event.location_f());
- // TODO(hirono): dnd_action() here may not be updated. Chrome needs to provide
- // a way to update DND action asynchronously.
- drag_info.drag_operation = static_cast<int>(
- DndActionToDragOperation(data_offer_->get()->dnd_action()));
- return drag_info;
- }
- void DataDevice::OnDragExited() {
- if (!data_offer_)
- return;
- delegate_->OnLeave();
- data_offer_.reset();
- }
- WMHelper::DragDropObserver::DropCallback DataDevice::GetDropCallback() {
- base::ScopedClosureRunner drag_exit(
- base::BindOnce(&DataDevice::OnDragExited, weak_factory_.GetWeakPtr()));
- return base::BindOnce(&DataDevice::PerformDropOrExitDrag,
- drop_weak_factory_.GetWeakPtr(), std::move(drag_exit));
- }
- void DataDevice::OnClipboardDataChanged() {
- if (!focused_surface_)
- return;
- SetSelectionToCurrentClipboardData();
- }
- void DataDevice::OnSurfaceFocused(Surface* gained_surface,
- Surface* lost_focused,
- bool has_focused_surface) {
- Surface* next_focused_surface =
- gained_surface && delegate_->CanAcceptDataEventsForSurface(gained_surface)
- ? gained_surface
- : nullptr;
- // Check if focused surface is not changed.
- if (focused_surface_ && focused_surface_->get() == next_focused_surface)
- return;
- std::unique_ptr<ScopedSurface> last_focused_surface =
- std::move(focused_surface_);
- focused_surface_ = next_focused_surface ? std::make_unique<ScopedSurface>(
- next_focused_surface, this)
- : nullptr;
- // Check if the client newly obtained focus.
- if (focused_surface_ && !last_focused_surface)
- SetSelectionToCurrentClipboardData();
- }
- void DataDevice::OnDataOfferDestroying(DataOffer* data_offer) {
- if (data_offer_ && data_offer_->get() == data_offer) {
- drop_succeeded_ = data_offer_->get()->finished();
- if (quit_closure_)
- std::move(quit_closure_).Run();
- data_offer_.reset();
- }
- drop_weak_factory_.InvalidateWeakPtrs();
- }
- void DataDevice::OnSurfaceDestroying(Surface* surface) {
- if (focused_surface_ && focused_surface_->get() == surface)
- focused_surface_.reset();
- }
- Surface* DataDevice::GetEffectiveTargetForEvent(
- const ui::DropTargetEvent& event) const {
- aura::Window* window = static_cast<aura::Window*>(event.target());
- if (!window)
- return nullptr;
- Surface* target = Surface::AsSurface(window);
- if (!target)
- return nullptr;
- return delegate_->CanAcceptDataEventsForSurface(target) ? target : nullptr;
- }
- void DataDevice::SetSelectionToCurrentClipboardData() {
- DCHECK(focused_surface_);
- DataOffer* data_offer = delegate_->OnDataOffer();
- data_offer->SetClipboardData(
- seat_->data_exchange_delegate(), *ui::Clipboard::GetForCurrentThread(),
- seat_->data_exchange_delegate()->GetDataTransferEndpointType(
- focused_surface_->get()->window()));
- delegate_->OnSelection(*data_offer);
- }
- void DataDevice::PerformDropOrExitDrag(
- base::ScopedClosureRunner exit_drag,
- ui::mojom::DragOperation& output_drag_op) {
- exit_drag.ReplaceClosure(base::DoNothing());
- if (!data_offer_) {
- output_drag_op = DragOperation::kNone;
- return;
- }
- DndAction dnd_action = data_offer_->get()->dnd_action();
- delegate_->OnDrop();
- // TODO(crbug.com/1160925): Avoid using nested loop by adding asynchronous
- // callback to aura::client::DragDropDelegate.
- base::WeakPtr<DataDevice> alive(weak_factory_.GetWeakPtr());
- base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, run_loop.QuitClosure(), kDataOfferDestructionTimeout);
- quit_closure_ = run_loop.QuitClosure();
- run_loop.Run();
- if (!alive) {
- output_drag_op = DragOperation::kNone;
- return;
- }
- if (quit_closure_) {
- // DataOffer not destroyed by the client until the timeout.
- quit_closure_.Reset();
- data_offer_.reset();
- drop_succeeded_ = false;
- }
- if (!drop_succeeded_)
- output_drag_op = DragOperation::kNone;
- else
- output_drag_op = DndActionToDragOperation(dnd_action);
- }
- } // namespace exo
|