Chromium Code Reviews| Index: ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc |
| diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..315356ad97a4a601234a4457348eb9419af29afc |
| --- /dev/null |
| +++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc |
| @@ -0,0 +1,678 @@ |
| +// Copyright 2014 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 <map> |
| +#include <vector> |
| +#include <X11/Xlib.h> |
| + |
| +// Include views_test_base.h first because the definition of None in X.h |
| +// conflicts with the definition of None in gtest-type-util.h |
| +#include "ui/views/test/views_test_base.h" |
| + |
| +#include "base/memory/scoped_ptr.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "ui/aura/window.h" |
| +#include "ui/aura/window_tree_host.h" |
| +#include "ui/base/dragdrop/os_exchange_data.h" |
| +#include "ui/base/x/x11_util.h" |
| +#include "ui/gfx/x/x11_atom_cache.h" |
| +#include "ui/gfx/x/x11_types.h" |
| +#include "ui/views/widget/desktop_aura/desktop_cursor_loader_updater.h" |
| +#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h" |
| +#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" |
| +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" |
| +#include "ui/views/widget/widget.h" |
| + |
| +namespace views { |
| + |
| +namespace { |
| + |
| +const char* kAtomsToCache[] = { |
| + "XdndActionCopy", |
| + "XdndDrop", |
| + "XdndEnter", |
| + "XdndFinished", |
| + "XdndLeave", |
| + "XdndPosition", |
| + "XdndStatus", |
| + "XdndTypeList", |
| + NULL |
| +}; |
| + |
| +class TestDragDropClient; |
| + |
| +// Collects messages which would otherwise be sent to |xid_| via |
| +// SendXClientEvent(). |
| +class ClientMessageEventCollector { |
| + public: |
| + ClientMessageEventCollector(::Window xid, TestDragDropClient* client); |
| + virtual ~ClientMessageEventCollector(); |
| + |
| + // Returns true if |events_| is non-empty. |
| + bool HasEvents() const { |
| + return !events_.empty(); |
| + } |
| + |
| + // Pops all of |events_| and returns the popped events in the order that they |
| + // were on the stack |
| + std::vector<XClientMessageEvent> PopAllEvents(); |
| + |
| + // Adds |event| to the stack. |
| + void RecordEvent(const XClientMessageEvent& event); |
| + |
| + private: |
| + ::Window xid_; |
| + |
| + // Not owned. |
| + TestDragDropClient* client_; |
| + |
| + std::vector<XClientMessageEvent> events_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ClientMessageEventCollector); |
| +}; |
| + |
| +// Implementation of DesktopDragDropClientAuraX11 which works with a fake |
| +// |DesktopDragDropClientAuraX11::source_current_window_|. |
| +class TestDragDropClient : public DesktopDragDropClientAuraX11 { |
| + public: |
| + // The location in screen coordinates used for the synthetic mouse moves |
| + // generated in SetTopmostXWindowAndMoveMouse(). |
| + static const int kMouseMoveX; |
| + static const int kMouseMoveY; |
| + |
| + TestDragDropClient(aura::Window* window, |
| + DesktopNativeCursorManager* cursor_manager); |
| + virtual ~TestDragDropClient(); |
| + |
| + // Returns the XID of the window which initiated the drag. |
| + ::Window source_xwindow() { |
| + return source_xid_; |
| + } |
| + |
| + // Returns the Atom with |name|. |
| + Atom GetAtom(const char* name); |
| + |
| + // Returns true if the event's message has |type|. |
| + bool MessageHasType(const XClientMessageEvent& event, |
| + const char* type); |
| + |
| + // Sets |collector| to collect XClientMessageEvents which would otherwise |
| + // have been sent to the drop target window. |
| + void SetEventCollectorFor(::Window xid, |
| + ClientMessageEventCollector* collector); |
| + |
| + // Builds an XdndStatus message and sends it to |
| + // DesktopDragDropClientAuraX11. |
| + void OnStatus(XID target_window, |
| + bool will_accept_drop, |
| + ::Atom accepted_action); |
| + |
| + // Builds an XdndFinished message and sends it to |
| + // DesktopDragDropClientAuraX11. |
| + void OnFinished(XID target_window, |
| + bool accepted_drop, |
| + ::Atom performed_action); |
| + |
| + // Sets |xid| as the topmost window at the current mouse position and |
| + // generates a synthetic mouse move. |
| + void SetTopmostXWindowAndMoveMouse(::Window xid); |
| + |
| + // Returns true if the move loop is running. |
| + bool IsMoveLoopRunning(); |
| + |
| + // DesktopDragDropClientAuraX11: |
| + virtual int StartDragAndDrop( |
| + const ui::OSExchangeData& data, |
| + aura::Window* root_window, |
| + aura::Window* source_window, |
| + const gfx::Point& root_location, |
| + int operation, |
| + ui::DragDropTypes::DragEventSource source) OVERRIDE; |
| + virtual void OnMoveLoopEnded() OVERRIDE; |
| + |
| + private: |
| + // DesktopDragDropClientAuraX11: |
| + virtual ::Window FindWindowFor(const gfx::Point& screen_point) OVERRIDE; |
| + virtual void SendXClientEvent(::Window xid, XEvent* event) OVERRIDE; |
| + |
| + // The XID of the window which initiated the drag. |
| + ::Window source_xid_; |
| + |
| + // The XID of the window which is simulated to be the topmost window at the |
| + // current mouse position. |
| + ::Window target_xid_; |
| + |
| + // Whether the move loop is running. |
| + bool move_loop_running_; |
| + |
| + // Map of ::Windows to the collector which intercepts XClientMessageEvents |
| + // for that window. |
| + std::map<::Window, ClientMessageEventCollector*> collectors_; |
| + |
| + ui::X11AtomCache atom_cache_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TestDragDropClient); |
| +}; |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| +// ClientMessageEventCollector |
| + |
| +ClientMessageEventCollector::ClientMessageEventCollector( |
| + ::Window xid, |
| + TestDragDropClient* client) |
| + : xid_(xid), |
| + client_(client) { |
| + client->SetEventCollectorFor(xid, this); |
| +} |
| + |
| +ClientMessageEventCollector::~ClientMessageEventCollector() { |
| + client_->SetEventCollectorFor(xid_, NULL); |
| +} |
| + |
| +std::vector<XClientMessageEvent> ClientMessageEventCollector::PopAllEvents() { |
| + std::vector<XClientMessageEvent> to_return; |
| + to_return.swap(events_); |
| + return to_return; |
| +} |
| + |
| +void ClientMessageEventCollector::RecordEvent( |
| + const XClientMessageEvent& event) { |
| + events_.push_back(event); |
| +} |
| + |
| +/////////////////////////////////////////////////////////////////////////////// |
| +// TestDragDropClient |
| + |
| +// static |
| +const int TestDragDropClient::kMouseMoveX = 100; |
| + |
| +// static |
| +const int TestDragDropClient::kMouseMoveY = 200; |
| + |
| +TestDragDropClient::TestDragDropClient( |
| + aura::Window* window, |
| + DesktopNativeCursorManager* cursor_manager) |
| + : DesktopDragDropClientAuraX11(window, |
| + cursor_manager, |
| + gfx::GetXDisplay(), |
| + window->GetHost()->GetAcceleratedWidget()), |
| + source_xid_(window->GetHost()->GetAcceleratedWidget()), |
| + target_xid_(None), |
| + move_loop_running_(false), |
| + atom_cache_(gfx::GetXDisplay(), kAtomsToCache) { |
| +} |
| + |
| +TestDragDropClient::~TestDragDropClient() { |
| +} |
| + |
| +Atom TestDragDropClient::GetAtom(const char* name) { |
| + return atom_cache_.GetAtom(name); |
| +} |
| + |
| +bool TestDragDropClient::MessageHasType(const XClientMessageEvent& event, |
| + const char* type) { |
| + return event.message_type == atom_cache_.GetAtom(type); |
| +} |
| + |
| +void TestDragDropClient::SetEventCollectorFor( |
| + ::Window xid, |
| + ClientMessageEventCollector* collector) { |
| + if (collector) |
| + collectors_[xid] = collector; |
| + else |
| + collectors_.erase(xid); |
| +} |
| + |
| +void TestDragDropClient::OnStatus(XID target_window, |
| + bool will_accept_drop, |
| + ::Atom accepted_action) { |
| + XClientMessageEvent event; |
| + event.message_type = atom_cache_.GetAtom("XdndStatus"); |
| + event.format = 32; |
| + event.window = source_xid_; |
| + event.data.l[0] = target_window; |
| + event.data.l[1] = will_accept_drop ? 1 : 0; |
| + event.data.l[2] = 0; |
| + event.data.l[3] = 0; |
| + event.data.l[4] = accepted_action; |
| + OnXdndStatus(event); |
| +} |
| + |
| +void TestDragDropClient::OnFinished(XID target_window, |
| + bool accepted_drop, |
| + ::Atom performed_action) { |
| + XClientMessageEvent event; |
| + event.message_type = atom_cache_.GetAtom("XdndFinished"); |
| + event.format = 32; |
| + event.window = source_xid_; |
| + event.data.l[0] = target_window; |
| + event.data.l[1] = accepted_drop ? 1 : 0; |
| + event.data.l[2] = performed_action; |
| + event.data.l[3] = 0; |
| + event.data.l[4] = 0; |
| + OnXdndFinished(event); |
| +} |
| + |
| +void TestDragDropClient::SetTopmostXWindowAndMoveMouse(::Window xid) { |
| + target_xid_ = xid; |
| + |
| + XMotionEvent event; |
| + event.time = CurrentTime; |
| + event.x_root = kMouseMoveX; |
| + event.y_root = kMouseMoveY; |
| + OnMouseMovement(&event); |
| +} |
| + |
| +bool TestDragDropClient::IsMoveLoopRunning() { |
| + return move_loop_running_; |
| +} |
| + |
| +int TestDragDropClient::StartDragAndDrop( |
| + const ui::OSExchangeData& data, |
| + aura::Window* root_window, |
| + aura::Window* source_window, |
| + const gfx::Point& root_location, |
| + int operation, |
| + ui::DragDropTypes::DragEventSource source) { |
| + move_loop_running_ = true; |
| + return DesktopDragDropClientAuraX11::StartDragAndDrop(data, root_window, |
| + source_window, root_location, operation, source); |
| +} |
| + |
| +void TestDragDropClient::OnMoveLoopEnded() { |
| + DesktopDragDropClientAuraX11::OnMoveLoopEnded(); |
| + move_loop_running_ = false; |
| +} |
| + |
| +::Window TestDragDropClient::FindWindowFor(const gfx::Point& screen_point) { |
| + return target_xid_; |
| +} |
| + |
| +void TestDragDropClient::SendXClientEvent(::Window xid, XEvent* event) { |
| + std::map<::Window, ClientMessageEventCollector*>::iterator it = |
| + collectors_.find(xid); |
| + if (it != collectors_.end()) |
| + it->second->RecordEvent(event->xclient); |
| +} |
| + |
| +} // namespace |
| + |
| +class DesktopDragDropClientAuraX11Test : public ViewsTestBase { |
| + public: |
| + DesktopDragDropClientAuraX11Test() { |
| + } |
| + |
| + virtual ~DesktopDragDropClientAuraX11Test() { |
| + } |
| + |
| + int StartDragAndDrop() { |
| + ui::OSExchangeData data; |
| + data.SetString(base::ASCIIToUTF16("Test")); |
| + |
| + return client_->StartDragAndDrop( |
| + data, |
| + widget_->GetNativeWindow()->GetRootWindow(), |
| + widget_->GetNativeWindow(), |
| + gfx::Point(), |
| + ui::DragDropTypes::DRAG_COPY, |
| + ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); |
| + } |
| + |
| + // ViewsTestBase: |
| + virtual void SetUp() OVERRIDE { |
| + ViewsTestBase::SetUp(); |
| + |
| + // Create widget to initiate the drags. |
| + widget_.reset(new Widget); |
| + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); |
| + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| + params.native_widget = new DesktopNativeWidgetAura(widget_.get()); |
| + params.bounds = gfx::Rect(100, 100); |
| + widget_->Init(params); |
| + widget_->Show(); |
| + |
| + cursor_manager_.reset(new DesktopNativeCursorManager( |
| + DesktopCursorLoaderUpdater::Create())); |
| + |
| + client_.reset(new TestDragDropClient(widget_->GetNativeWindow(), |
| + cursor_manager_.get())); |
| + } |
| + |
| + virtual void TearDown() OVERRIDE { |
| + client_.reset(); |
| + cursor_manager_.reset(); |
| + widget_.reset(); |
| + ViewsTestBase::TearDown(); |
| + } |
| + |
| + TestDragDropClient* client() { |
| + return client_.get(); |
| + } |
| + |
| + private: |
| + scoped_ptr<TestDragDropClient> client_; |
| + scoped_ptr<DesktopNativeCursorManager> cursor_manager_; |
| + |
| + // The widget used to initiate drags. |
| + scoped_ptr<Widget> widget_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11Test); |
| +}; |
| + |
| +namespace { |
| + |
| +void BasicStep2(TestDragDropClient* client, XID toplevel) { |
| + EXPECT_TRUE(client->IsMoveLoopRunning()); |
| + |
| + ClientMessageEventCollector collector(toplevel, client); |
| + client->SetTopmostXWindowAndMoveMouse(toplevel); |
| + |
| + // XdndEnter should have been sent to |toplevel| before the XdndPosition |
| + // message. |
| + std::vector<XClientMessageEvent> events = collector.PopAllEvents(); |
| + ASSERT_EQ(2u, events.size()); |
| + |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); |
| + EXPECT_EQ(client->source_xwindow(), |
| + static_cast<XID>(events[0].data.l[0])); |
| + EXPECT_EQ(1, events[0].data.l[1] & 1); |
| + std::vector<Atom> targets; |
| + ui::GetAtomArrayProperty(client->source_xwindow(), "XdndTypeList", &targets); |
| + EXPECT_FALSE(targets.empty()); |
| + |
| + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); |
| + EXPECT_EQ(client->source_xwindow(), |
| + static_cast<XID>(events[0].data.l[0])); |
| + const long kCoords = |
| + TestDragDropClient::kMouseMoveX << 16 | TestDragDropClient::kMouseMoveY; |
| + EXPECT_EQ(kCoords, events[1].data.l[2]); |
| + EXPECT_EQ(client->GetAtom("XdndActionCopy"), |
| + static_cast<Atom>(events[1].data.l[4])); |
| + |
| + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); |
| + |
| + // Because there is no unprocessed XdndPosition, the drag drop client should |
| + // send XdndDrop immediately after the mouse is released. |
| + client->OnMouseReleased(); |
| + |
| + events = collector.PopAllEvents(); |
| + ASSERT_EQ(1u, events.size()); |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); |
| + EXPECT_EQ(client->source_xwindow(), |
| + static_cast<XID>(events[0].data.l[0])); |
| + |
| + // Send XdndFinished to indicate that the drag drop client can cleanup any |
| + // data related to this drag. The move loop should end only after the |
| + // XdndFinished message was received. |
| + EXPECT_TRUE(client->IsMoveLoopRunning()); |
| + client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy")); |
| + EXPECT_FALSE(client->IsMoveLoopRunning()); |
| +} |
| + |
| +void BasicStep3(TestDragDropClient* client, XID toplevel) { |
| + EXPECT_TRUE(client->IsMoveLoopRunning()); |
| + |
| + ClientMessageEventCollector collector(toplevel, client); |
| + client->SetTopmostXWindowAndMoveMouse(toplevel); |
| + |
| + std::vector<XClientMessageEvent> events = collector.PopAllEvents(); |
| + ASSERT_EQ(2u, events.size()); |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); |
| + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); |
| + |
| + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); |
| + client->SetTopmostXWindowAndMoveMouse(toplevel); |
| + events = collector.PopAllEvents(); |
| + ASSERT_EQ(1u, events.size()); |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition")); |
| + |
| + // We have not received an XdndStatus ack for the second XdndPosition message. |
| + // Test that sending XdndDrop is delayed till the XdndStatus ack is received. |
| + client->OnMouseReleased(); |
| + EXPECT_FALSE(collector.HasEvents()); |
| + |
| + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); |
| + events = collector.PopAllEvents(); |
| + ASSERT_EQ(1u, events.size()); |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); |
| + |
| + EXPECT_TRUE(client->IsMoveLoopRunning()); |
| + client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy")); |
| + EXPECT_FALSE(client->IsMoveLoopRunning()); |
| +} |
| + |
| +} // namespace |
| + |
| +TEST_F(DesktopDragDropClientAuraX11Test, Basic) { |
| + XID toplevel = 1; |
| + |
| + base::MessageLoop::current()->PostTask(FROM_HERE, |
| + base::Bind(&BasicStep2, |
| + client(), |
| + toplevel)); |
| + int result = StartDragAndDrop(); |
| + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); |
| + |
| + // Do another drag and drop to test that the data is properly cleaned up as a |
| + // result of the XdndFinished message. |
| + base::MessageLoop::current()->PostTask(FROM_HERE, |
| + base::Bind(&BasicStep3, |
| + client(), |
| + toplevel)); |
| + result = StartDragAndDrop(); |
| + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); |
| +} |
| + |
| +namespace { |
| + |
| +void TargetDoesNotRespondStep2(TestDragDropClient* client) { |
| + EXPECT_TRUE(client->IsMoveLoopRunning()); |
| + |
| + XID toplevel = 1; |
| + ClientMessageEventCollector collector(toplevel, client); |
| + client->SetTopmostXWindowAndMoveMouse(toplevel); |
| + |
| + std::vector<XClientMessageEvent> events = collector.PopAllEvents(); |
| + ASSERT_EQ(2u, events.size()); |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); |
| + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); |
| + |
| + client->OnMouseReleased(); |
| + events = collector.PopAllEvents(); |
| + ASSERT_EQ(1u, events.size()); |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndLeave")); |
| + EXPECT_FALSE(client->IsMoveLoopRunning()); |
| +} |
| + |
| +} // namespace |
| + |
| +// Test that we do not wait for the target to send XdndStatus if we have not |
| +// received any XdndStatus messages at all from the target. The Unity |
| +// DNDCollectionWindow is an example of an XdndAware target which does not |
| +// respond to XdndPosition messages at all. |
| +TEST_F(DesktopDragDropClientAuraX11Test, TargetDoesNotRespond) { |
| + base::MessageLoop::current()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&TargetDoesNotRespondStep2, client())); |
| + int result = StartDragAndDrop(); |
| + EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); |
| +} |
| + |
| +namespace { |
| + |
| +void QueuePositionStep2(TestDragDropClient* client) { |
| + EXPECT_TRUE(client->IsMoveLoopRunning()); |
| + |
| + XID toplevel = 1; |
| + ClientMessageEventCollector collector(toplevel, client); |
| + client->SetTopmostXWindowAndMoveMouse(toplevel); |
| + client->SetTopmostXWindowAndMoveMouse(toplevel); |
| + client->SetTopmostXWindowAndMoveMouse(toplevel); |
| + |
| + std::vector<XClientMessageEvent> events = collector.PopAllEvents(); |
| + ASSERT_EQ(2u, events.size()); |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); |
| + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); |
| + |
| + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); |
| + events = collector.PopAllEvents(); |
| + ASSERT_EQ(1u, events.size()); |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition")); |
| + |
| + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); |
| + EXPECT_FALSE(collector.HasEvents()); |
| + |
| + client->OnMouseReleased(); |
| + events = collector.PopAllEvents(); |
| + ASSERT_EQ(1u, events.size()); |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndDropped")); |
| + |
| + EXPECT_TRUE(client->IsMoveLoopRunning()); |
| + client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy")); |
| + EXPECT_FALSE(client->IsMoveLoopRunning()); |
| +} |
| + |
| +} // namespace |
| + |
| +// Test that XdndPosition messages are queued till the pending XdndPosition |
| +// message is acked via an XdndStatus message. |
| +TEST_F(DesktopDragDropClientAuraX11Test, QueuePosition) { |
| + base::MessageLoop::current()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&QueuePositionStep2, client())); |
| + int result = StartDragAndDrop(); |
| + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); |
| +} |
| + |
| +namespace { |
| + |
| +void TargetChangesStep2(TestDragDropClient* client) { |
| + EXPECT_TRUE(client->IsMoveLoopRunning()); |
| + |
| + XID toplevel1 = 1; |
| + ClientMessageEventCollector collector1(toplevel1, client); |
| + client->SetTopmostXWindowAndMoveMouse(toplevel1); |
| + |
| + std::vector<XClientMessageEvent> events1 = collector1.PopAllEvents(); |
| + ASSERT_EQ(2u, events1.size()); |
| + EXPECT_TRUE(client->MessageHasType(events1[0], "XdndEnter")); |
| + EXPECT_TRUE(client->MessageHasType(events1[1], "XdndPosition")); |
| + |
| + XID toplevel2 = 2; |
| + ClientMessageEventCollector collector2(toplevel2, client); |
| + client->SetTopmostXWindowAndMoveMouse(toplevel2); |
| + |
| + // It is possible for |toplevel1| to send XdndStatus after the source has sent |
| + // XdndLeave but before |toplevel1| has received the XdndLeave message. The |
| + // XdndStatus message should be ignored. |
| + client->OnStatus(toplevel1, true, client->GetAtom("XdndActionCopy")); |
| + events1 = collector1.PopAllEvents(); |
| + ASSERT_EQ(1u, events1.size()); |
| + EXPECT_TRUE(client->MessageHasType(events1[0], "XdndLeave")); |
| + |
| + std::vector<XClientMessageEvent> events2 = collector2.PopAllEvents(); |
|
varkha
2014/05/07 03:11:08
Nit: Could use the same events vector for collecti
pkotwicz
2014/05/07 15:16:36
I think that keeping the vectors separate is clear
|
| + ASSERT_EQ(2u, events2.size()); |
| + EXPECT_TRUE(client->MessageHasType(events2[0], "XdndEnter")); |
| + EXPECT_TRUE(client->MessageHasType(events2[1], "XdndPosition")); |
| + |
| + client->OnStatus(toplevel2, true, client->GetAtom("XdndActionCopy")); |
| + client->OnMouseReleased(); |
| + events2 = collector2.PopAllEvents(); |
| + ASSERT_EQ(1u, events2.size()); |
| + EXPECT_TRUE(client->MessageHasType(events2[0], "XdndDrop")); |
| + |
| + EXPECT_TRUE(client->IsMoveLoopRunning()); |
| + client->OnFinished(toplevel2, true, client->GetAtom("XdndActionCopy")); |
| + EXPECT_FALSE(client->IsMoveLoopRunning()); |
| +} |
| + |
| +} // namespace |
| + |
| +// Test the behavior when the target changes during a drag. |
| +TEST_F(DesktopDragDropClientAuraX11Test, TargetChanges) { |
| + base::MessageLoop::current()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&TargetChangesStep2, client())); |
| + int result = StartDragAndDrop(); |
| + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); |
| +} |
| + |
| +namespace { |
| + |
| +void RejectAfterMouseReleaseStep2(TestDragDropClient* client) { |
| + EXPECT_TRUE(client->IsMoveLoopRunning()); |
| + |
| + XID toplevel = 1; |
| + ClientMessageEventCollector collector(toplevel, client); |
| + client->SetTopmostXWindowAndMoveMouse(toplevel); |
| + |
| + std::vector<XClientMessageEvent> events = collector.PopAllEvents(); |
| + ASSERT_EQ(2u, events.size()); |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); |
| + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); |
| + |
| + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); |
| + EXPECT_FALSE(collector.HasEvents()); |
| + |
| + // Send another mouse move such that there is a pending XdndPosition. |
| + client->SetTopmostXWindowAndMoveMouse(toplevel); |
| + events = collector.PopAllEvents(); |
| + ASSERT_EQ(1u, events.size()); |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition")); |
| + |
| + client->OnMouseReleased(); |
| + // Reject the drop. |
| + client->OnStatus(toplevel, false, None); |
| + |
| + events = collector.PopAllEvents(); |
| + ASSERT_EQ(1u, events.size()); |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndLeave")); |
| + EXPECT_FALSE(client->IsMoveLoopRunning()); |
| +} |
| + |
| +void RejectAfterMouseReleaseStep3(TestDragDropClient* client) { |
| + EXPECT_TRUE(client->IsMoveLoopRunning()); |
| + |
| + XID toplevel = 2; |
| + ClientMessageEventCollector collector(toplevel, client); |
| + client->SetTopmostXWindowAndMoveMouse(toplevel); |
| + |
| + std::vector<XClientMessageEvent> events = collector.PopAllEvents(); |
| + ASSERT_EQ(2u, events.size()); |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); |
| + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); |
| + |
| + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); |
| + EXPECT_FALSE(collector.HasEvents()); |
| + |
| + client->OnMouseReleased(); |
| + events = collector.PopAllEvents(); |
| + ASSERT_EQ(1u, events.size()); |
| + EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); |
| + |
| + EXPECT_TRUE(client->IsMoveLoopRunning()); |
| + client->OnFinished(toplevel, false, None); |
| + EXPECT_FALSE(client->IsMoveLoopRunning()); |
| +} |
| + |
| +} // namespace |
| + |
| +// Test that the source sends XdndLeave instead of XdndDrop if the drag |
| +// operation is rejected after the mouse is released. |
| +TEST_F(DesktopDragDropClientAuraX11Test, RejectAfterMouseRelease) { |
| + base::MessageLoop::current()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&RejectAfterMouseReleaseStep2, client())); |
| + int result = StartDragAndDrop(); |
| + EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); |
| + |
| + // Repeat the test but reject the drop in the XdndFinished message instead. |
| + base::MessageLoop::current()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&RejectAfterMouseReleaseStep3, client())); |
| + result = StartDragAndDrop(); |
| + EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); |
| +} |
| + |
| +} // namespace views |