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..de8a5ef53ec682735bbbfec7bf20b3a2bd0d3a66 |
--- /dev/null |
+++ b/services/ui/ws/drag_controller_unittest.cc |
@@ -0,0 +1,583 @@ |
+// 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" |
+ |
+#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_drop_start() { |
+ return times_received_drag_drop_start_; |
+ } |
+ |
+ 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 PerformOnDragDropStart( |
+ mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data) override { |
+ times_received_drag_drop_start_++; |
+ 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 PerformOnDragDropDone() 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_drop_start_ = 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->PerformOnDragDropStart(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* GetDragTargetForWindow( |
+ 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_drop_start()); |
+ StartDragOperation(std::move(mime_data), window1.get(), |
+ ui::mojom::kDropEffectMove); |
+ EXPECT_EQ(1u, window1->times_received_drag_drop_start()); |
+ DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
+ gfx::Point(1, 1)); |
+ EXPECT_EQ(1u, window1->times_received_drag_drop_start()); |
+ window1->Respond(true); |
+ |
+ // Window2 doesn't receive the drag data until mouse is over it. |
+ EXPECT_EQ(0u, window2->times_received_drag_drop_start()); |
+ DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON, |
+ gfx::Point(2, 2)); |
+ EXPECT_EQ(1u, window2->times_received_drag_drop_start()); |
+ |
+ // 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_drop_start()); |
+ |
+ // 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_drop_start()); |
+} |
+ |
+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_drop_start()); |
+} |
+ |
+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 |