Chromium Code Reviews| Index: services/ui/ws/drag_controller_unittest.cc |
| diff --git a/services/ui/ws/drag_controller_unittest.cc b/services/ui/ws/drag_controller_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..53bf9b669c100c666176e37453266695f6538217 |
| --- /dev/null |
| +++ b/services/ui/ws/drag_controller_unittest.cc |
| @@ -0,0 +1,582 @@ |
| +// Copyright 2016 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 "services/ui/ws/drag_controller.h" |
|
sky
2016/09/13 18:15:31
newline between 5/6.
|
| +#include "services/ui/ws/drag_source.h" |
| +#include "services/ui/ws/drag_target_connection.h" |
| +#include "services/ui/ws/ids.h" |
| +#include "services/ui/ws/server_window.h" |
| +#include "services/ui/ws/test_server_window_delegate.h" |
| +#include "services/ui/ws/test_utils.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| +#include "ui/events/base_event_utils.h" |
| + |
| +namespace ui { |
| +namespace ws { |
| + |
| +enum class QueuedType { NONE, ENTER, OVER, LEAVE, DROP }; |
| + |
| +class DragControllerTest; |
| + |
| +// All the classes to represent a window. |
| +class DragTestWindow : public DragTargetConnection { |
| + public: |
| + struct DragEvent { |
| + QueuedType type; |
| + uint32_t key_state; |
| + gfx::Point cursor_offset; |
| + uint32_t effect_bitmask; |
| + base::Callback<void(uint32_t)> callback; |
| + }; |
| + |
| + DragTestWindow(DragControllerTest* parent, const WindowId& id) |
| + : parent_(parent), window_delegate_(), window_(&window_delegate_, id) { |
| + window_.SetCanAcceptDrops(true); |
| + } |
| + ~DragTestWindow() override; |
| + |
| + TestServerWindowDelegate* delegate() { return &window_delegate_; } |
| + ServerWindow* window() { return &window_; } |
| + |
| + QueuedType queue_response_type() { |
| + if (queued_callbacks_.empty()) |
| + return QueuedType::NONE; |
| + return queued_callbacks_.front().type; |
| + } |
| + |
| + const DragEvent& queue_front() { return queued_callbacks_.front(); } |
| + |
| + size_t queue_size() { return queued_callbacks_.size(); } |
| + |
| + uint32_t times_received_drag_mime_types() { |
| + return times_received_drag_mime_types_; |
| + } |
| + |
| + void SetParent(DragTestWindow* p) { p->window_.Add(&window_); } |
| + |
| + void OptOutOfDrag() { window_.SetCanAcceptDrops(false); } |
| + |
| + // Calls the callback at the front of the queue. |
| + void Respond(bool respond_with_effect) { |
| + if (queued_callbacks_.size()) { |
| + if (!queued_callbacks_.front().callback.is_null()) { |
| + queued_callbacks_.front().callback.Run( |
| + respond_with_effect ? queued_callbacks_.front().effect_bitmask : 0); |
| + } |
| + |
| + queued_callbacks_.pop(); |
| + } |
| + } |
| + |
| + // Overridden from DragTestConnection: |
| + void PerformOnDragMimeTypes( |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data) override { |
| + times_received_drag_mime_types_++; |
| + mime_data_ = std::move(mime_data); |
| + } |
| + |
| + void PerformOnDragEnter( |
| + const ServerWindow* window, |
| + uint32_t key_state, |
| + const gfx::Point& cursor_offset, |
| + uint32_t effect_bitmask, |
| + const base::Callback<void(uint32_t)>& callback) override { |
| + DCHECK_EQ(window, &window_); |
| + queued_callbacks_.push({QueuedType::ENTER, key_state, cursor_offset, |
| + effect_bitmask, callback}); |
| + } |
| + |
| + void PerformOnDragOver( |
| + const ServerWindow* window, |
| + uint32_t key_state, |
| + const gfx::Point& cursor_offset, |
| + uint32_t effect_bitmask, |
| + const base::Callback<void(uint32_t)>& callback) override { |
| + DCHECK_EQ(window, &window_); |
| + queued_callbacks_.push( |
| + {QueuedType::OVER, key_state, cursor_offset, effect_bitmask, callback}); |
| + } |
| + |
| + void PerformOnDragLeave(const ServerWindow* window) override { |
| + DCHECK_EQ(window, &window_); |
| + queued_callbacks_.push({QueuedType::LEAVE, 0, gfx::Point(), 0, |
| + base::Callback<void(uint32_t)>()}); |
| + } |
| + |
| + void PerformOnCompleteDrop( |
| + const ServerWindow* window, |
| + uint32_t key_state, |
| + const gfx::Point& cursor_offset, |
| + uint32_t effect_bitmask, |
| + const base::Callback<void(uint32_t)>& callback) override { |
| + DCHECK_EQ(window, &window_); |
| + queued_callbacks_.push( |
| + {QueuedType::DROP, key_state, cursor_offset, effect_bitmask, callback}); |
| + } |
| + |
| + void PerformOnDragFinish() override { mime_data_.SetToEmpty(); } |
| + |
| + private: |
| + DragControllerTest* parent_; |
| + TestServerWindowDelegate window_delegate_; |
| + ServerWindow window_; |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data_; |
| + uint32_t times_received_drag_mime_types_ = 0; |
| + |
| + std::queue<DragEvent> queued_callbacks_; |
| +}; |
| + |
| +class DragControllerTest : public testing::Test, public DragSource { |
| + public: |
| + std::unique_ptr<DragTestWindow> BuildWindow() { |
| + WindowId id(1, ++window_id_); |
| + std::unique_ptr<DragTestWindow> p = |
| + base::MakeUnique<DragTestWindow>(this, id); |
| + server_window_by_id_[id] = p->window(); |
| + connection_by_window_[p->window()] = p.get(); |
| + return p; |
| + } |
| + |
| + void StartDragOperation( |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data, |
| + DragTestWindow* window, |
| + uint32_t drag_operations) { |
| + window->PerformOnDragMimeTypes(mime_data.Clone()); |
| + drag_operation_.reset(new DragController( |
| + this, window->window(), window, PointerEvent::kMousePointerId, |
| + std::move(mime_data), drag_operations)); |
| + } |
| + |
| + void DispatchDrag(DragTestWindow* window, |
| + bool mouse_released, |
| + uint32_t flags, |
| + const gfx::Point& position) { |
| + ui::PointerEvent event( |
| + ui::MouseEvent(mouse_released ? ET_MOUSE_RELEASED : ET_MOUSE_PRESSED, |
| + position, position, ui::EventTimeForNow(), flags, 0)); |
| + drag_operation_->DispatchPointerEvent(event, |
| + window ? window->window() : nullptr); |
| + } |
| + |
| + void DispatchDragWithPointer(DragTestWindow* window, |
| + int32_t drag_pointer, |
| + bool mouse_released, |
| + uint32_t flags, |
| + const gfx::Point& position) { |
| + ui::PointerEvent event(ET_POINTER_DOWN, position, position, flags, |
| + drag_pointer, 0, PointerDetails(), |
| + base::TimeTicks()); |
| + drag_operation_->DispatchPointerEvent(event, |
| + window ? window->window() : nullptr); |
| + } |
| + |
| + void OnTestWindowDestroyed(DragTestWindow* test_window) { |
| + drag_operation_->OnWillDestroyDragTargetConnection(test_window); |
| + server_window_by_id_.erase(test_window->window()->id()); |
| + connection_by_window_.erase(test_window->window()); |
| + } |
| + |
| + DragController* drag_operation() const { return drag_operation_.get(); } |
| + const base::Optional<bool>& drag_completed_value() { |
| + return drag_completed_value_; |
| + } |
| + |
| + private: |
| + // Overridden from testing::Test: |
| + void SetUp() override { |
| + testing::Test::SetUp(); |
| + |
| + window_delegate_.reset(new TestServerWindowDelegate()); |
| + root_window_.reset( |
| + new ServerWindow(window_delegate_.get(), WindowId(1, 2))); |
| + window_delegate_->set_root_window(root_window_.get()); |
| + root_window_->SetVisible(true); |
| + } |
| + |
| + void TearDown() override { |
| + drag_operation_.reset(); |
| + root_window_.reset(); |
| + window_delegate_.reset(); |
| + |
| + DCHECK(server_window_by_id_.empty()); |
| + DCHECK(connection_by_window_.empty()); |
| + |
| + testing::Test::TearDown(); |
| + } |
| + |
| + // Overridden from DragControllerSource: |
| + void OnDragCompleted(bool success) override { |
| + drag_completed_value_ = success; |
| + } |
| + |
| + ServerWindow* GetWindowById(const WindowId& id) override { |
| + auto it = server_window_by_id_.find(id); |
| + if (it == server_window_by_id_.end()) |
| + return nullptr; |
| + return it->second; |
| + } |
| + |
| + DragTargetConnection* GetDragTargetWithRoot( |
| + const ServerWindow* window) override { |
| + auto it = connection_by_window_.find(const_cast<ServerWindow*>(window)); |
| + if (it == connection_by_window_.end()) |
| + return nullptr; |
| + return it->second; |
| + } |
| + |
| + int window_id_ = 3; |
| + |
| + std::map<WindowId, ServerWindow*> server_window_by_id_; |
| + std::map<ServerWindow*, DragTargetConnection*> connection_by_window_; |
| + |
| + std::unique_ptr<TestServerWindowDelegate> window_delegate_; |
| + std::unique_ptr<ServerWindow> root_window_; |
| + |
| + std::unique_ptr<DragController> drag_operation_; |
| + |
| + base::Optional<bool> drag_completed_value_; |
| +}; |
| + |
| +DragTestWindow::~DragTestWindow() { |
| + parent_->OnTestWindowDestroyed(this); |
| +} |
| + |
| +TEST_F(DragControllerTest, SimpleDragDrop) { |
| + std::unique_ptr<DragTestWindow> window = BuildWindow(); |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data; |
| + StartDragOperation(std::move(mime_data), window.get(), |
| + ui::mojom::kDropEffectMove); |
| + |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1)); |
| + EXPECT_EQ(QueuedType::ENTER, window->queue_response_type()); |
| + window->Respond(true); |
| + |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(2, 2)); |
| + EXPECT_EQ(QueuedType::OVER, window->queue_response_type()); |
| + window->Respond(true); |
| + |
| + DispatchDrag(window.get(), true, 0, gfx::Point(2, 2)); |
| + EXPECT_EQ(QueuedType::DROP, window->queue_response_type()); |
| + window->Respond(true); |
| + |
| + EXPECT_TRUE(drag_completed_value().value_or(false)); |
| +} |
| + |
| +TEST_F(DragControllerTest, OnlyDeliverMimeDataOnce) { |
| + std::unique_ptr<DragTestWindow> window1 = BuildWindow(); |
| + std::unique_ptr<DragTestWindow> window2 = BuildWindow(); |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data; |
| + |
| + // The client lib is responsible for sending the data to the window that's |
| + // the drag source to minimize IPC. |
| + EXPECT_EQ(0u, window1->times_received_drag_mime_types()); |
| + StartDragOperation(std::move(mime_data), window1.get(), |
| + ui::mojom::kDropEffectMove); |
| + EXPECT_EQ(1u, window1->times_received_drag_mime_types()); |
| + DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(1, 1)); |
| + EXPECT_EQ(1u, window1->times_received_drag_mime_types()); |
| + window1->Respond(true); |
| + |
| + // Window2 doesn't receive the drag data until mouse is over it. |
| + EXPECT_EQ(0u, window2->times_received_drag_mime_types()); |
| + DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(2, 2)); |
| + EXPECT_EQ(1u, window2->times_received_drag_mime_types()); |
| + |
| + // Moving back to the source window doesn't send an additional start message. |
| + DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(1, 1)); |
| + EXPECT_EQ(1u, window1->times_received_drag_mime_types()); |
| + |
| + // Moving back to window2 doesn't send an additional start message. |
| + DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(1, 1)); |
| + EXPECT_EQ(1u, window2->times_received_drag_mime_types()); |
| +} |
| + |
| +TEST_F(DragControllerTest, DeliverMessageToParent) { |
| + std::unique_ptr<DragTestWindow> window1 = BuildWindow(); |
| + std::unique_ptr<DragTestWindow> window2 = BuildWindow(); |
| + std::unique_ptr<DragTestWindow> window3 = BuildWindow(); |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data; |
| + |
| + window3->SetParent(window2.get()); |
| + window3->OptOutOfDrag(); |
| + |
| + StartDragOperation(std::move(mime_data), window1.get(), |
| + ui::mojom::kDropEffectMove); |
| + |
| + // Dispatching a drag to window3 (which has can accept drags off) redirects |
| + // to window2, which is its parent. |
| + DispatchDrag(window3.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(1, 1)); |
| + EXPECT_EQ(1u, window2->times_received_drag_mime_types()); |
| +} |
| + |
| +TEST_F(DragControllerTest, FailWhenDropOverNoWindow) { |
| + std::unique_ptr<DragTestWindow> window = BuildWindow(); |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data; |
| + StartDragOperation(std::move(mime_data), window.get(), |
| + ui::mojom::kDropEffectMove); |
| + |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1)); |
| + EXPECT_EQ(QueuedType::ENTER, window->queue_response_type()); |
| + window->Respond(true); |
| + |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(2, 2)); |
| + EXPECT_EQ(QueuedType::OVER, window->queue_response_type()); |
| + window->Respond(true); |
| + |
| + DispatchDrag(nullptr, true, 0, gfx::Point(2, 2)); |
| + // Moving outside of |window| should result in |window| getting a leave. |
| + EXPECT_EQ(QueuedType::LEAVE, window->queue_response_type()); |
| + window->Respond(true); |
| + |
| + EXPECT_FALSE(drag_completed_value().value_or(true)); |
| +} |
| + |
| +TEST_F(DragControllerTest, EnterLeaveWhenMovingBetweenTwoWindows) { |
| + std::unique_ptr<DragTestWindow> window1 = BuildWindow(); |
| + std::unique_ptr<DragTestWindow> window2 = BuildWindow(); |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data; |
| + StartDragOperation(std::move(mime_data), window1.get(), |
| + ui::mojom::kDropEffectMove); |
| + |
| + DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(1, 1)); |
| + EXPECT_EQ(QueuedType::ENTER, window1->queue_response_type()); |
| + window1->Respond(true); |
| + |
| + DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(2, 2)); |
| + EXPECT_EQ(QueuedType::ENTER, window2->queue_response_type()); |
| + EXPECT_EQ(QueuedType::LEAVE, window1->queue_response_type()); |
| + window1->Respond(true); |
| + window2->Respond(true); |
| +} |
| + |
| +TEST_F(DragControllerTest, DeadWindowDoesntBlock) { |
| + std::unique_ptr<DragTestWindow> window1 = BuildWindow(); |
| + std::unique_ptr<DragTestWindow> window2 = BuildWindow(); |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data; |
| + StartDragOperation(std::move(mime_data), window1.get(), |
| + ui::mojom::kDropEffectMove); |
| + |
| + test::DragControllerTestApi api(drag_operation()); |
| + |
| + // Simulate a dead window by giving it a few messages, but don't respond to |
| + // them. |
| + DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(1, 1)); |
| + DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(2, 2)); |
| + EXPECT_EQ(1u, window1->queue_size()); |
| + EXPECT_EQ(2u, api.GetSizeOfQueueForWindow(window1->window())); |
| + |
| + // Moving to window2 should dispatch the enter event to it immediately. |
| + DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(3, 3)); |
| + EXPECT_EQ(QueuedType::ENTER, window2->queue_response_type()); |
| + EXPECT_EQ(1u, window1->queue_size()); |
| +} |
| + |
| +TEST_F(DragControllerTest, EnterToOverQueued) { |
| + std::unique_ptr<DragTestWindow> window = BuildWindow(); |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data; |
| + StartDragOperation(std::move(mime_data), window.get(), |
| + ui::mojom::kDropEffectMove); |
| + |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1)); |
| + ASSERT_EQ(1u, window->queue_size()); |
| + EXPECT_EQ(QueuedType::ENTER, window->queue_response_type()); |
| + // Don't respond. |
| + |
| + // We don't receive another message since we haven't acknowledged the first. |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 2)); |
| + ASSERT_EQ(1u, window->queue_size()); |
| + |
| + // Responding causes us to receive our next event. |
| + window->Respond(true); |
| + ASSERT_EQ(1u, window->queue_size()); |
| + EXPECT_EQ(QueuedType::OVER, window->queue_response_type()); |
| +} |
| + |
| +TEST_F(DragControllerTest, CoalesceMouseOverEvents) { |
| + std::unique_ptr<DragTestWindow> window = BuildWindow(); |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data; |
| + StartDragOperation(std::move(mime_data), window.get(), |
| + ui::mojom::kDropEffectMove); |
| + |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1)); |
| + EXPECT_EQ(QueuedType::ENTER, window->queue_response_type()); |
| + |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 2)); |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(2, 2)); |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(2, 3)); |
| + |
| + // Responding to the first delivers us the last mouse over event's position. |
| + window->Respond(true); |
| + ASSERT_EQ(1u, window->queue_size()); |
| + EXPECT_EQ(QueuedType::OVER, window->queue_response_type()); |
| + EXPECT_EQ(gfx::Point(2, 3), window->queue_front().cursor_offset); |
| + |
| + // There are no queued events because they were coalesced. |
| + window->Respond(true); |
| + EXPECT_EQ(0u, window->queue_size()); |
| +} |
| + |
| +TEST_F(DragControllerTest, RemovePendingMouseOversOnLeave) { |
| + std::unique_ptr<DragTestWindow> window1 = BuildWindow(); |
| + std::unique_ptr<DragTestWindow> window2 = BuildWindow(); |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data; |
| + StartDragOperation(std::move(mime_data), window1.get(), |
| + ui::mojom::kDropEffectMove); |
| + |
| + // Enter |
| + DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(1, 1)); |
| + EXPECT_EQ(QueuedType::ENTER, window1->queue_response_type()); |
| + |
| + // Over |
| + DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(1, 1)); |
| + |
| + // Leave |
| + DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(1, 1)); |
| + |
| + // The window finally responds to the enter message; we should not receive |
| + // any over messages since we didn't respond to the enter message in time. |
| + window1->Respond(true); |
| + ASSERT_EQ(1u, window1->queue_size()); |
| + EXPECT_EQ(QueuedType::LEAVE, window1->queue_response_type()); |
| +} |
| + |
| +TEST_F(DragControllerTest, TargetWindowClosedWhileDrag) { |
| + std::unique_ptr<DragTestWindow> window1 = BuildWindow(); |
| + std::unique_ptr<DragTestWindow> window2 = BuildWindow(); |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data; |
| + StartDragOperation(std::move(mime_data), window1.get(), |
| + ui::mojom::kDropEffectMove); |
| + |
| + test::DragControllerTestApi api(drag_operation()); |
| + |
| + // Send some events to |window|. |
| + DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(1, 1)); |
| + EXPECT_EQ(QueuedType::ENTER, window2->queue_response_type()); |
| + DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(1, 1)); |
| + |
| + ServerWindow* server_window = window2->window(); |
| + |
| + // Ensure that DragController is waiting for a response from |window|. |
| + EXPECT_EQ(2u, api.GetSizeOfQueueForWindow(server_window)); |
| + EXPECT_EQ(server_window, api.GetCurrentTarget()); |
| + |
| + // Force the destruction of |window.window|. |
| + window2.reset(); |
| + |
| + // DragController doesn't know anything about the server window now. |
| + EXPECT_EQ(0u, api.GetSizeOfQueueForWindow(server_window)); |
| + EXPECT_EQ(nullptr, api.GetCurrentTarget()); |
| + |
| + // But a target window closing out from under us doesn't fail the drag. |
| + EXPECT_FALSE(drag_completed_value().has_value()); |
| +} |
| + |
| +TEST_F(DragControllerTest, SourceWindowClosedWhileDrag) { |
| + std::unique_ptr<DragTestWindow> window1 = BuildWindow(); |
| + std::unique_ptr<DragTestWindow> window2 = BuildWindow(); |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data; |
| + StartDragOperation(std::move(mime_data), window1.get(), |
| + ui::mojom::kDropEffectMove); |
| + |
| + test::DragControllerTestApi api(drag_operation()); |
| + |
| + // Send some events to |window|. |
| + DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(1, 1)); |
| + EXPECT_EQ(QueuedType::ENTER, window2->queue_response_type()); |
| + DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(1, 1)); |
| + |
| + ServerWindow* server_window = window2->window(); |
| + |
| + // Ensure that DragController is waiting for a response from |window|. |
| + EXPECT_EQ(2u, api.GetSizeOfQueueForWindow(server_window)); |
| + EXPECT_EQ(server_window, api.GetCurrentTarget()); |
| + |
| + // Force the destruction of the source window. |
| + window1.reset(); |
| + |
| + // The source window going away fails the drag. |
| + EXPECT_FALSE(drag_completed_value().value_or(true)); |
| +} |
| + |
| +TEST_F(DragControllerTest, DontQueueEventsAfterDrop) { |
| + // The DragController needs to stick around to coordinate the drop, but |
| + // it should ignore further mouse events during this time. |
| + std::unique_ptr<DragTestWindow> window = BuildWindow(); |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data; |
| + StartDragOperation(std::move(mime_data), window.get(), |
| + ui::mojom::kDropEffectMove); |
| + |
| + test::DragControllerTestApi api(drag_operation()); |
| + |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1)); |
| + EXPECT_EQ(QueuedType::ENTER, window->queue_response_type()); |
| + window->Respond(true); |
| + |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(2, 2)); |
| + EXPECT_EQ(QueuedType::OVER, window->queue_response_type()); |
| + window->Respond(true); |
| + |
| + DispatchDrag(window.get(), true, 0, gfx::Point(2, 2)); |
| + EXPECT_EQ(QueuedType::DROP, window->queue_response_type()); |
| + EXPECT_EQ(1u, api.GetSizeOfQueueForWindow(window->window())); |
| + |
| + // Further located events don't result in additional drag messages. |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(2, 2)); |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(2, 2)); |
| + EXPECT_EQ(1u, api.GetSizeOfQueueForWindow(window->window())); |
| +} |
| + |
| +TEST_F(DragControllerTest, CancelDrag) { |
| + // The DragController needs to stick around to coordinate the drop, but |
| + // it should ignore further mouse events during this time. |
| + std::unique_ptr<DragTestWindow> window = BuildWindow(); |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data; |
| + StartDragOperation(std::move(mime_data), window.get(), |
| + ui::mojom::kDropEffectMove); |
| + |
| + DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1)); |
| + EXPECT_EQ(QueuedType::ENTER, window->queue_response_type()); |
| + window->Respond(true); |
| + |
| + drag_operation()->Cancel(); |
| + |
| + EXPECT_FALSE(drag_completed_value().value_or(true)); |
| +} |
| + |
| +TEST_F(DragControllerTest, IgnoreEventsFromOtherPointers) { |
| + std::unique_ptr<DragTestWindow> window = BuildWindow(); |
| + mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data; |
| + // This starts the operation with PointerEvent::kMousePointerId. |
| + StartDragOperation(std::move(mime_data), window.get(), |
| + ui::mojom::kDropEffectMove); |
| + |
| + // Ignore events from pointer 5. |
| + DispatchDragWithPointer(window.get(), 5, false, ui::EF_LEFT_MOUSE_BUTTON, |
| + gfx::Point(1, 1)); |
| + ASSERT_EQ(0u, window->queue_size()); |
| +} |
| + |
| +// TODO(erg): Add a test to ensure windows that the cursor isn't over |
| +// responding to messages don't change the cursor when we have cursor handling |
| +// code. |
| + |
| +} // namespace ws |
| +} // namespace ui |