| 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..4286d1873a51805d09053f9c4e900e3213a46332
|
| --- /dev/null
|
| +++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc
|
| @@ -0,0 +1,679 @@
|
| +// 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 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"
|
| +
|
| +#include <X11/Xlib.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], "XdndDrop"));
|
| +
|
| + 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();
|
| + 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
|
|
|