Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(293)

Unified Diff: ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc

Issue 268083002: Add tests for drag and drop for Linux Aura (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698