Commit b6a5909d authored by Xiyuan Xia's avatar Xiyuan Xia Committed by Commit Bot

ws2: Wire the start drag code path

- Add RunDragLoop and CancelDragLoop to WindowServiceDelegate interface;
- WindowServiceDelegateImpl use ash::DragDropController to provide
  the actual impl;
- WindowTree implements PerformDragDrop and CancelDragDrop via
  the WindowServiceDelegate;

Bug: 837716
Test: WindowServiceDelegateImplTest.*Drag*, WindowTreeTest2.*Drag*
Change-Id: Id5d609d30dd53171b01bfeec60e4547f7a61b4c3
Reviewed-on: https://chromium-review.googlesource.com/1110669
Commit-Queue: Xiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#569418}
parent 28a311ff
......@@ -34,23 +34,33 @@ void OnMoveLoopCompleted(base::OnceCallback<void(bool success)> end_closure,
.Run(result == wm::WmToplevelWindowEventHandler::DragResult::SUCCESS);
}
// Returns true if a window move loop can be started.
bool ShouldStartMoveLoop(aura::Window* window,
ui::mojom::MoveLoopSource source) {
// A window move can not be started while drag and drop is in progress.
// Returns true if there is a drag and drop in progress.
bool InDragLoop(aura::Window* window) {
aura::client::DragDropClient* drag_drop_client =
aura::client::GetDragDropClient(window->GetRootWindow());
if (drag_drop_client && drag_drop_client->IsDragDropInProgress())
return false;
ToplevelWindowEventHandler* event_handler =
Shell::Get()->toplevel_window_event_handler();
// Only one move is allowed at a time.
if (event_handler->wm_toplevel_window_event_handler()
->is_drag_in_progress()) {
return false;
}
return true;
return drag_drop_client && drag_drop_client->IsDragDropInProgress();
}
// Returns true if there is a window move loop in progress.
bool InWindowMoveLoop() {
return Shell::Get()
->toplevel_window_event_handler()
->wm_toplevel_window_event_handler()
->is_drag_in_progress();
}
// Returns true if a window move loop can be started.
bool ShouldStartMoveLoop(aura::Window* window) {
// A window move can only be started when there is no in progress drag loop or
// window move loop.
return !InDragLoop(window) && !InWindowMoveLoop();
}
// Returns true if a drag loop can be started.
bool ShouldStartDragLoop(aura::Window* window) {
// A drag loop can only be started when there is no in progress drag loop or
// window move loop.
return !InDragLoop(window) && !InWindowMoveLoop();
}
} // namespace
......@@ -94,7 +104,7 @@ void WindowServiceDelegateImpl::RunWindowMoveLoop(
ui::mojom::MoveLoopSource source,
const gfx::Point& cursor,
DoneCallback callback) {
if (!ShouldStartMoveLoop(window, source)) {
if (!ShouldStartMoveLoop(window)) {
std::move(callback).Run(false);
return;
}
......@@ -121,4 +131,32 @@ void WindowServiceDelegateImpl::CancelWindowMoveLoop() {
->RevertDrag();
}
void WindowServiceDelegateImpl::RunDragLoop(
aura::Window* window,
const ui::OSExchangeData& data,
const gfx::Point& screen_location,
uint32_t drag_operation,
ui::DragDropTypes::DragEventSource source,
DragDropCompletedCallback callback) {
if (!ShouldStartDragLoop(window)) {
std::move(callback).Run(ui::DragDropTypes::DRAG_NONE);
return;
}
aura::Window* const root_window = window->GetRootWindow();
aura::client::DragDropClient* drag_drop_client =
aura::client::GetDragDropClient(root_window);
DCHECK(drag_drop_client);
std::move(callback).Run(drag_drop_client->StartDragAndDrop(
data, root_window, window, screen_location, drag_operation, source));
}
void WindowServiceDelegateImpl::CancelDragLoop(aura::Window* window) {
if (!InDragLoop(window))
return;
aura::client::GetDragDropClient(window->GetRootWindow())->DragCancel();
}
} // namespace ash
......@@ -28,6 +28,13 @@ class WindowServiceDelegateImpl : public ui::ws2::WindowServiceDelegate {
const gfx::Point& cursor,
DoneCallback callback) override;
void CancelWindowMoveLoop() override;
void RunDragLoop(aura::Window* window,
const ui::OSExchangeData& data,
const gfx::Point& screen_location,
uint32_t drag_operation,
ui::DragDropTypes::DragEventSource source,
DragDropCompletedCallback callback) override;
void CancelDragLoop(aura::Window* window) override;
private:
DISALLOW_COPY_AND_ASSIGN(WindowServiceDelegateImpl);
......
......@@ -5,13 +5,43 @@
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/toplevel_window_event_handler.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "services/ui/ws2/test_window_tree_client.h"
#include "services/ui/ws2/window_tree_test_helper.h"
#include "ui/aura/client/drag_drop_client.h"
#include "ui/aura/client/drag_drop_delegate.h"
#include "ui/aura/window.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/events/test/event_generator.h"
namespace ash {
namespace {
// A testing DragDropDelegate that accepts any drops.
class TestDragDropDelegate : public aura::client::DragDropDelegate {
public:
TestDragDropDelegate() = default;
~TestDragDropDelegate() override = default;
// aura::client::DragDropDelegate:
void OnDragEntered(const ui::DropTargetEvent& event) override {}
int OnDragUpdated(const ui::DropTargetEvent& event) override {
return ui::DragDropTypes::DRAG_MOVE;
}
void OnDragExited() override {}
int OnPerformDrop(const ui::DropTargetEvent& event) override {
return ui::DragDropTypes::DRAG_MOVE;
}
private:
DISALLOW_COPY_AND_ASSIGN(TestDragDropDelegate);
};
} // namespace
// This test creates a top-level window via the WindowService in SetUp() and
// provides some ease of use functions to access WindowService related state.
class WindowServiceDelegateImplTest : public AshTestBase {
......@@ -33,6 +63,16 @@ class WindowServiceDelegateImplTest : public AshTestBase {
return GetTestWindowTreeClient()->tracker()->changes();
}
void SetCanAcceptDrops() {
aura::client::SetDragDropDelegate(top_level_.get(),
&test_drag_drop_delegate_);
}
bool IsDragDropInProgress() {
return aura::client::GetDragDropClient(top_level_->GetRootWindow())
->IsDragDropInProgress();
}
// AshTestBase:
void SetUp() override {
AshTestBase::SetUp();
......@@ -49,6 +89,7 @@ class WindowServiceDelegateImplTest : public AshTestBase {
}
protected:
TestDragDropDelegate test_drag_drop_delegate_;
std::unique_ptr<aura::Window> top_level_;
private:
......@@ -99,4 +140,111 @@ TEST_F(WindowServiceDelegateImplTest, CancelWindowMoveLoop) {
EXPECT_EQ(gfx::Point(100, 100), top_level_->bounds().origin());
}
TEST_F(WindowServiceDelegateImplTest, RunDragLoop) {
SetCanAcceptDrops();
GetWindowTreeTestHelper()->window_tree()->PerformDragDrop(
21, GetTopLevelWindowId(), gfx::Point(),
base::flat_map<std::string, std::vector<uint8_t>>(), gfx::ImageSkia(),
gfx::Vector2d(), 0, ::ui::mojom::PointerKind::MOUSE);
base::RunLoop run_loop;
// Post mouse move and release to allow the nested drag loop to pick it up.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindLambdaForTesting([this] {
ASSERT_TRUE(IsDragDropInProgress());
// Move mouse to center of |top_level_|.
GetEventGenerator().MoveMouseTo(gfx::Point(150, 150));
GetWindowTreeClientChanges()->clear();
GetEventGenerator().ReleaseLeftButton();
}));
// Let the drop loop task run.
run_loop.RunUntilIdle();
EXPECT_TRUE(
ContainsChange(*GetWindowTreeClientChanges(),
"OnPerformDragDropCompleted id=21 success=true action=1"));
}
TEST_F(WindowServiceDelegateImplTest, DeleteWindowWithInProgressDragLoop) {
SetCanAcceptDrops();
GetWindowTreeTestHelper()->window_tree()->PerformDragDrop(
21, GetTopLevelWindowId(), gfx::Point(),
base::flat_map<std::string, std::vector<uint8_t>>(), gfx::ImageSkia(),
gfx::Vector2d(), 0, ::ui::mojom::PointerKind::MOUSE);
base::RunLoop run_loop;
// Post the task to delete the window to allow nested drag loop to pick it up.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindLambdaForTesting([this] {
ASSERT_TRUE(IsDragDropInProgress());
// Deletes the window.
top_level_.reset();
// Move mouse and release the button and it should not crash.
GetEventGenerator().MoveMouseTo(gfx::Point(150, 150));
GetWindowTreeClientChanges()->clear();
GetEventGenerator().ReleaseLeftButton();
}));
// Let the drop loop task run.
run_loop.RunUntilIdle();
// It fails because the target window |top_level_| is deleted.
EXPECT_TRUE(ContainsChange(
*GetWindowTreeClientChanges(),
"OnPerformDragDropCompleted id=21 success=false action=0"));
}
TEST_F(WindowServiceDelegateImplTest, CancelDragDropBeforeDragLoopRun) {
SetCanAcceptDrops();
GetWindowTreeTestHelper()->window_tree()->PerformDragDrop(
21, GetTopLevelWindowId(), gfx::Point(),
base::flat_map<std::string, std::vector<uint8_t>>(), gfx::ImageSkia(),
gfx::Vector2d(), 0, ::ui::mojom::PointerKind::MOUSE);
// Cancel the drag before the drag loop runs.
GetWindowTreeTestHelper()->window_tree()->CancelDragDrop(
GetTopLevelWindowId());
// Let the drop loop task run.
base::RunLoop().RunUntilIdle();
// It fails because the drag is canceled.
EXPECT_TRUE(ContainsChange(
*GetWindowTreeClientChanges(),
"OnPerformDragDropCompleted id=21 success=false action=0"));
}
TEST_F(WindowServiceDelegateImplTest, CancelDragDropAfterDragLoopRun) {
SetCanAcceptDrops();
GetWindowTreeTestHelper()->window_tree()->PerformDragDrop(
21, GetTopLevelWindowId(), gfx::Point(),
base::flat_map<std::string, std::vector<uint8_t>>(), gfx::ImageSkia(),
gfx::Vector2d(), 0, ::ui::mojom::PointerKind::MOUSE);
base::RunLoop run_loop;
// Post the task to cancel to allow nested drag loop to pick it up.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindLambdaForTesting([this] {
ASSERT_TRUE(IsDragDropInProgress());
GetWindowTreeTestHelper()->window_tree()->CancelDragDrop(
GetTopLevelWindowId());
}));
// Let the drop loop task run.
run_loop.RunUntilIdle();
// It fails because the drag is canceled.
EXPECT_TRUE(ContainsChange(
*GetWindowTreeClientChanges(),
"OnPerformDragDropCompleted id=21 success=false action=0"));
}
} // namespace ash
......@@ -175,6 +175,11 @@ std::string ChangeToDescription(const Change& change,
WindowIdToString(change.window_id).c_str());
case CHANGE_TYPE_DRAG_DROP_DONE:
return "DragDropDone";
case CHANGE_TYPE_ON_PERFORM_DRAG_DROP_COMPLETED:
return base::StringPrintf(
"OnPerformDragDropCompleted id=%d success=%s action=%d",
change.change_id, change.bool_value ? "true" : "false",
change.drag_drop_action);
}
return std::string();
}
......@@ -558,6 +563,17 @@ void TestChangeTracker::OnDragDropDone() {
AddChange(change);
}
void TestChangeTracker::OnPerformDragDropCompleted(uint32_t change_id,
bool success,
uint32_t action_taken) {
Change change;
change.type = CHANGE_TYPE_ON_PERFORM_DRAG_DROP_COMPLETED;
change.change_id = change_id;
change.bool_value = success;
change.drag_drop_action = action_taken;
AddChange(change);
}
void TestChangeTracker::RequestClose(Id window_id) {
Change change;
change.type = CHANGE_TYPE_REQUEST_CLOSE;
......
......@@ -54,6 +54,7 @@ enum ChangeType {
CHANGE_TYPE_DRAG_LEAVE,
CHANGE_TYPE_COMPLETE_DROP,
CHANGE_TYPE_DRAG_DROP_DONE,
CHANGE_TYPE_ON_PERFORM_DRAG_DROP_COMPLETED,
};
// TODO(sky): consider nuking and converting directly to WindowData.
......@@ -111,6 +112,7 @@ struct Change {
gfx::Point location1;
gfx::PointF location2;
base::flat_map<std::string, std::vector<uint8_t>> drag_data;
uint32_t drag_drop_action;
};
// The ChangeToDescription related functions convert a Change into a string.
......@@ -226,6 +228,9 @@ class TestChangeTracker {
void OnDragLeave(Id widnow_id);
void OnCompleteDrop(Id window_id);
void OnDragDropDone();
void OnPerformDragDropCompleted(uint32_t change_id,
bool success,
uint32_t action_taken);
void RequestClose(Id window_id);
private:
......
......@@ -21,6 +21,11 @@ TestWindowServiceDelegate::TakeMoveLoopCallback() {
return std::move(move_loop_callback_);
}
WindowServiceDelegate::DragDropCompletedCallback
TestWindowServiceDelegate::TakeDragLoopCallback() {
return std::move(drag_loop_callback_);
}
std::unique_ptr<aura::Window> TestWindowServiceDelegate::NewTopLevel(
aura::PropertyConverter* property_converter,
const base::flat_map<std::string, std::vector<uint8_t>>& properties) {
......@@ -52,5 +57,19 @@ void TestWindowServiceDelegate::CancelWindowMoveLoop() {
cancel_window_move_loop_called_ = true;
}
void TestWindowServiceDelegate::RunDragLoop(
aura::Window* window,
const ui::OSExchangeData& data,
const gfx::Point& screen_location,
uint32_t drag_operation,
ui::DragDropTypes::DragEventSource source,
DragDropCompletedCallback callback) {
drag_loop_callback_ = std::move(callback);
}
void TestWindowServiceDelegate::CancelDragLoop(aura::Window* window) {
cancel_drag_loop_called_ = true;
}
} // namespace ws2
} // namespace ui
......@@ -43,6 +43,10 @@ class TestWindowServiceDelegate : public WindowServiceDelegate {
DoneCallback TakeMoveLoopCallback();
bool cancel_drag_loop_called() const { return cancel_drag_loop_called_; }
DragDropCompletedCallback TakeDragLoopCallback();
// WindowServiceDelegate:
std::unique_ptr<aura::Window> NewTopLevel(
aura::PropertyConverter* property_converter,
......@@ -54,6 +58,13 @@ class TestWindowServiceDelegate : public WindowServiceDelegate {
const gfx::Point& cursor,
DoneCallback callback) override;
void CancelWindowMoveLoop() override;
void RunDragLoop(aura::Window* window,
const ui::OSExchangeData& data,
const gfx::Point& screen_location,
uint32_t drag_operation,
ui::DragDropTypes::DragEventSource source,
DragDropCompletedCallback callback) override;
void CancelDragLoop(aura::Window* window) override;
private:
aura::Window* top_level_parent_;
......@@ -62,10 +73,14 @@ class TestWindowServiceDelegate : public WindowServiceDelegate {
// Callback supplied to RunWindowMoveLoop() is set here.
DoneCallback move_loop_callback_;
// Callback supplied to RunDragLoop() is set here.
DragDropCompletedCallback drag_loop_callback_;
// Events passed to OnUnhandledKeyEvent() are added here.
std::vector<KeyEvent> unhandled_key_events_;
bool cancel_window_move_loop_called_ = false;
bool cancel_drag_loop_called_ = false;
DISALLOW_COPY_AND_ASSIGN(TestWindowServiceDelegate);
};
......
......@@ -278,7 +278,9 @@ void TestWindowTreeClient::OnCompleteDrop(Id window,
void TestWindowTreeClient::OnPerformDragDropCompleted(uint32_t change_id,
bool success,
uint32_t action_taken) {}
uint32_t action_taken) {
tracker_.OnPerformDragDropCompleted(change_id, success, action_taken);
}
void TestWindowTreeClient::OnDragDropDone() {
tracker_.OnDragDropDone();
......
......@@ -19,5 +19,15 @@ void WindowServiceDelegate::RunWindowMoveLoop(aura::Window* window,
std::move(callback).Run(false);
}
void WindowServiceDelegate::RunDragLoop(
aura::Window* window,
const ui::OSExchangeData& data,
const gfx::Point& screen_location,
uint32_t drag_operation,
ui::DragDropTypes::DragEventSource source,
DragDropCompletedCallback callback) {
std::move(callback).Run(ui::DragDropTypes::DRAG_NONE);
}
} // namespace ws2
} // namespace ui
......@@ -16,6 +16,7 @@
#include "base/containers/flat_map.h"
#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/dragdrop/drag_drop_types.h"
namespace aura {
class PropertyConverter;
......@@ -29,6 +30,7 @@ class Point;
namespace ui {
class KeyEvent;
class OSExchangeData;
namespace ws2 {
......@@ -68,6 +70,23 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowServiceDelegate {
// RunWindowMoveLoop().
virtual void CancelWindowMoveLoop() {}
// Called to run a drag loop for |window|. When done, |callback| should be
// invoked with the |drag_result|. |drag_result| == DRAG_NONE means drag
// failed or is canceled. Otherwise, it the final drag operation applied at
// the end. If a drag is not allowed, the delegate should run |callback|
// immediately. Note this call blocks until the drag operation is finished or
// canceled.
using DragDropCompletedCallback = base::OnceCallback<void(int drag_result)>;
virtual void RunDragLoop(aura::Window* window,
const ui::OSExchangeData& data,
const gfx::Point& screen_location,
uint32_t drag_operation,
ui::DragDropTypes::DragEventSource source,
DragDropCompletedCallback callback);
// Called to cancel an in-progress drag loop that was started by RunDragLoop.
virtual void CancelDragLoop(aura::Window* window) {}
protected:
virtual ~WindowServiceDelegate() = default;
};
......
......@@ -8,6 +8,7 @@
#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/unguessable_token.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/common/surfaces/surface_info.h"
......@@ -25,16 +26,20 @@
#include "services/ui/ws2/window_service_delegate.h"
#include "ui/aura/client/transient_window_client.h"
#include "ui/aura/env.h"
#include "ui/aura/mus/os_exchange_data_provider_mus.h"
#include "ui/aura/mus/property_converter.h"
#include "ui/aura/mus/property_utils.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_type.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/wm/core/capture_controller.h"
namespace ui {
......@@ -319,6 +324,53 @@ void WindowTree::OnPerformWindowMoveDone(uint32_t change_id, bool result) {
window_tree_client_->OnChangeCompleted(change_id, result);
}
void WindowTree::DoPerformDragDrop(
uint32_t change_id,
Id source_window_id,
const gfx::Point& screen_location,
const base::flat_map<std::string, std::vector<uint8_t>>& drag_data,
const gfx::ImageSkia& drag_image,
const gfx::Vector2d& drag_image_offset,
uint32_t drag_operation,
::ui::mojom::PointerKind source) {
if (pending_drag_source_window_id_ != source_window_id) {
// Pending drag is canceled before DoPerformDragDrop runs.
window_tree_client_->OnPerformDragDropCompleted(change_id, false,
mojom::kDropEffectNone);
return;
}
aura::Window* source_window = GetWindowByTransportId(source_window_id);
if (!source_window) {
DVLOG(1) << "PerformDragDrop failed (no window)";
OnPerformDragDropDone(change_id, mojom::kDropEffectNone);
return;
}
if (!IsClientCreatedWindow(source_window)) {
DVLOG(1) << "PerformDragDrop failed (access denied)";
OnPerformDragDropDone(change_id, mojom::kDropEffectNone);
return;
}
ui::OSExchangeData data(std::make_unique<aura::OSExchangeDataProviderMus>(
mojo::FlatMapToMap(drag_data)));
data.provider().SetDragImage(drag_image, drag_image_offset);
window_service_->delegate()->RunDragLoop(
source_window, data, screen_location, drag_operation,
source == ::ui::mojom::PointerKind::MOUSE
? ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE
: ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH,
base::BindOnce(&WindowTree::OnPerformDragDropDone,
weak_factory_.GetWeakPtr(), change_id));
}
void WindowTree::OnPerformDragDropDone(uint32_t change_id, int drag_result) {
pending_drag_source_window_id_ = kInvalidTransportId;
window_tree_client_->OnPerformDragDropCompleted(
change_id, drag_result != ui::DragDropTypes::DRAG_NONE, drag_result);
}
aura::Window* WindowTree::AddClientCreatedWindow(
const ClientWindowId& id,
bool is_top_level,
......@@ -1520,11 +1572,40 @@ void WindowTree::PerformDragDrop(
const gfx::Vector2d& drag_image_offset,
uint32_t drag_operation,
::ui::mojom::PointerKind source) {
NOTIMPLEMENTED_LOG_ONCE();
if (pending_drag_source_window_id_ != kInvalidTransportId) {
DVLOG(1) << "PerformDragDrop failed (only one drag allowed)";
window_tree_client_->OnPerformDragDropCompleted(change_id, false,
mojom::kDropEffectNone);
return;
}
pending_drag_source_window_id_ = source_window_id;
// Runs the drag loop as a posted task to unwind mojo call stack.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&WindowTree::DoPerformDragDrop, weak_factory_.GetWeakPtr(),
change_id, source_window_id, screen_location, drag_data,
drag_image, drag_image_offset, drag_operation, source));
}
void WindowTree::CancelDragDrop(Id window_id) {
NOTIMPLEMENTED_LOG_ONCE();
if (pending_drag_source_window_id_ == kInvalidTransportId) {
DVLOG(1) << "CancelDragDrop called and a drag is not underway";
return;
}
if (pending_drag_source_window_id_ != window_id) {
DVLOG(1) << "CancelDragDrop called with wrong window";
return;
}
// Clear |pending_drag_source_window_id_| to cancel posted drag loop task.
pending_drag_source_window_id_ = kInvalidTransportId;
// Cancel the current drag loop if it is running.
window_service_->delegate()->CancelDragLoop(
GetWindowByTransportId(window_id));
}
} // namespace ws2
......
......@@ -162,6 +162,23 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowTree
// the move loop (true for success).
void OnPerformWindowMoveDone(uint32_t change_id, bool result);
// Scheduled from PerformDragDrop to run drag loop with mojo stack unwinded.
void DoPerformDragDrop(
uint32_t change_id,
Id source_window_id,
const gfx::Point& screen_location,
const base::flat_map<std::string, std::vector<uint8_t>>& drag_data,
const gfx::ImageSkia& drag_image,
const gfx::Vector2d& drag_image_offset,
uint32_t drag_operation,
::ui::mojom::PointerKind source);
// Callback from WindowServiceDelegate::RunDragLoop(). |change_id| is
// the id at the time the drag loop was initiated. |result| is the result of
// the drag (the final drag operation performed, or DRAG_NONE when the drag
// fails or gets canceled).
void OnPerformDragDropDone(uint32_t change_id, int drag_result);
// Called for windows created by the client (including top-levels).
aura::Window* AddClientCreatedWindow(
const ClientWindowId& id,
......@@ -449,6 +466,9 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowTree
// Set while a window move loop is in progress.
aura::Window* window_moving_ = nullptr;
// Set while a drag loop is in progress.
Id pending_drag_source_window_id_ = kInvalidTransportId;
base::WeakPtrFactory<WindowTree> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(WindowTree);
......
......@@ -1475,6 +1475,173 @@ TEST(WindowTreeTest2, CancelMoveLoop) {
SingleChangeToDescription(*setup.changes()));
}
TEST(WindowTreeTest2, PerformDragDrop) {
WindowServiceTestSetup setup;
aura::Window* top_level =
setup.window_tree_test_helper()->NewTopLevelWindow();
ASSERT_TRUE(top_level);
top_level->Show();
const Id top_level_id =
setup.window_tree_test_helper()->TransportIdForWindow(top_level);
setup.changes()->clear();
setup.window_tree_test_helper()->window_tree()->PerformDragDrop(
12, top_level_id, gfx::Point(),
base::flat_map<std::string, std::vector<uint8_t>>(), gfx::ImageSkia(),
gfx::Vector2d(), 0, ::ui::mojom::PointerKind::MOUSE);
// Let the posted drag loop task run.
base::RunLoop().RunUntilIdle();
// WindowServiceDelegate should be asked to run the drag loop.
WindowServiceDelegate::DragDropCompletedCallback drag_loop_callback =
setup.delegate()->TakeDragLoopCallback();
ASSERT_TRUE(drag_loop_callback);
// As the drag is in progress, changes should be empty.
EXPECT_TRUE(setup.changes()->empty());
// Respond with a drop operation, client should be notified with success.
std::move(drag_loop_callback).Run(ui::DragDropTypes::DRAG_MOVE);
EXPECT_EQ("OnPerformDragDropCompleted id=12 success=true action=1",
SingleChangeToDescription(*setup.changes()));
// Starts another drag and but the drag is canceled this time.
setup.changes()->clear();
setup.window_tree_test_helper()->window_tree()->PerformDragDrop(
13, top_level_id, gfx::Point(),
base::flat_map<std::string, std::vector<uint8_t>>(), gfx::ImageSkia(),
gfx::Vector2d(), 0, ::ui::mojom::PointerKind::MOUSE);
base::RunLoop().RunUntilIdle();
drag_loop_callback = setup.delegate()->TakeDragLoopCallback();
ASSERT_TRUE(drag_loop_callback);
std::move(drag_loop_callback).Run(ui::DragDropTypes::DRAG_NONE);
EXPECT_EQ("OnPerformDragDropCompleted id=13 success=false action=0",
SingleChangeToDescription(*setup.changes()));
}
TEST(WindowTreeTest2, PerformDragDropBeforePreviousOneFinish) {
WindowServiceTestSetup setup;
aura::Window* top_level =
setup.window_tree_test_helper()->NewTopLevelWindow();
ASSERT_TRUE(top_level);
top_level->Show();
const Id top_level_id =
setup.window_tree_test_helper()->TransportIdForWindow(top_level);
setup.changes()->clear();
setup.window_tree_test_helper()->window_tree()->PerformDragDrop(
12, top_level_id, gfx::Point(),
base::flat_map<std::string, std::vector<uint8_t>>(), gfx::ImageSkia(),
gfx::Vector2d(), 0, ::ui::mojom::PointerKind::MOUSE);
// PerformDragDrop before the drag loop task runs should fail.
setup.window_tree_test_helper()->window_tree()->PerformDragDrop(
13, top_level_id, gfx::Point(),
base::flat_map<std::string, std::vector<uint8_t>>(), gfx::ImageSkia(),
gfx::Vector2d(), 0, ::ui::mojom::PointerKind::MOUSE);
EXPECT_EQ("OnPerformDragDropCompleted id=13 success=false action=0",
SingleChangeToDescription(*setup.changes()));
// Let the posted drag loop task run.
base::RunLoop().RunUntilIdle();
// WindowServiceDelegate should be asked to run the drag loop.
WindowServiceDelegate::DragDropCompletedCallback drag_loop_callback =
setup.delegate()->TakeDragLoopCallback();
ASSERT_TRUE(drag_loop_callback);
// PerformDragDrop after the drop loop task runs should fail too because
// the drag is not finished.
setup.changes()->clear();
setup.window_tree_test_helper()->window_tree()->PerformDragDrop(
14, top_level_id, gfx::Point(),
base::flat_map<std::string, std::vector<uint8_t>>(), gfx::ImageSkia(),
gfx::Vector2d(), 0, ::ui::mojom::PointerKind::MOUSE);
EXPECT_EQ("OnPerformDragDropCompleted id=14 success=false action=0",
SingleChangeToDescription(*setup.changes()));
// Finish the drop operation, client should be notified with success.
setup.changes()->clear();
std::move(drag_loop_callback).Run(ui::DragDropTypes::DRAG_MOVE);
EXPECT_EQ("OnPerformDragDropCompleted id=12 success=true action=1",
SingleChangeToDescription(*setup.changes()));
}
TEST(WindowTreeTest2, CancelDragDrop) {
WindowServiceTestSetup setup;
aura::Window* top_level =
setup.window_tree_test_helper()->NewTopLevelWindow();
ASSERT_TRUE(top_level);
top_level->Show();
const Id top_level_id =
setup.window_tree_test_helper()->TransportIdForWindow(top_level);
setup.changes()->clear();
setup.window_tree_test_helper()->window_tree()->PerformDragDrop(
12, top_level_id, gfx::Point(),
base::flat_map<std::string, std::vector<uint8_t>>(), gfx::ImageSkia(),
gfx::Vector2d(), 0, ::ui::mojom::PointerKind::MOUSE);
// Let the posted drag loop task run.
base::RunLoop().RunUntilIdle();
// WindowServiceDelegate should be asked to run the drag loop.
WindowServiceDelegate::DragDropCompletedCallback drag_loop_callback =
setup.delegate()->TakeDragLoopCallback();
ASSERT_TRUE(drag_loop_callback);
// Cancelling with an invalid id should do nothing.
EXPECT_FALSE(setup.delegate()->cancel_drag_loop_called());
setup.window_tree_test_helper()->window_tree()->CancelDragDrop(
kInvalidTransportId);
EXPECT_TRUE(setup.changes()->empty());
EXPECT_FALSE(setup.delegate()->cancel_drag_loop_called());
// Cancel with the real id should notify the delegate.
EXPECT_FALSE(setup.delegate()->cancel_drag_loop_called());
setup.window_tree_test_helper()->window_tree()->CancelDragDrop(top_level_id);
EXPECT_TRUE(setup.delegate()->cancel_drag_loop_called());
// No changes yet because the |drag_loop_callback| has not run.
EXPECT_TRUE(setup.changes()->empty());
// Run the closure to simulate drag cancel.
std::move(drag_loop_callback).Run(ui::DragDropTypes::DRAG_NONE);
EXPECT_EQ("OnPerformDragDropCompleted id=12 success=false action=0",
SingleChangeToDescription(*setup.changes()));
}
TEST(WindowTreeTest2, CancelDragDropBeforeDragLoopRun) {
WindowServiceTestSetup setup;
aura::Window* top_level =
setup.window_tree_test_helper()->NewTopLevelWindow();
ASSERT_TRUE(top_level);
top_level->Show();
const Id top_level_id =
setup.window_tree_test_helper()->TransportIdForWindow(top_level);
setup.changes()->clear();
setup.window_tree_test_helper()->window_tree()->PerformDragDrop(
12, top_level_id, gfx::Point(),
base::flat_map<std::string, std::vector<uint8_t>>(), gfx::ImageSkia(),
gfx::Vector2d(), 0, ::ui::mojom::PointerKind::MOUSE);
// Cancel the drag before the drag loop task runs.
EXPECT_FALSE(setup.delegate()->cancel_drag_loop_called());
setup.window_tree_test_helper()->window_tree()->CancelDragDrop(top_level_id);
EXPECT_TRUE(setup.delegate()->cancel_drag_loop_called());
// Let the posted drag loop task run.
base::RunLoop().RunUntilIdle();
// WindowServiceDelegate should not be notified.
WindowServiceDelegate::DragDropCompletedCallback drag_loop_callback =
setup.delegate()->TakeDragLoopCallback();
EXPECT_FALSE(drag_loop_callback);
// The request should fail.
EXPECT_EQ("OnPerformDragDropCompleted id=12 success=false action=0",
SingleChangeToDescription(*setup.changes()));
}
} // namespace
} // namespace ws2
} // namespace ui
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment