Commit f9617ddd authored by Alexander Dunaev's avatar Alexander Dunaev Committed by Commit Bot

Extracted the X11DragDropClient tests.

This CL is one more step towards unification of classes used on Linux
with X11.

The lower level of the drag and drop on X11 is handling the XDND
protocol, which is done by ui::XDragDropClient; the latter is currently
used for both non-Ozone and Ozone paths, but historically it had been
part of DesktopDragDropClientAuraX11, so there were no tests for it.

Here DesktopDragDropClientAuraX11 tests that in fact test the XDND level
are moved out and test ui::XDragDropClient directly.  The higher level
ones (like, for example, DesktopDragDropClientAuraX11Test.HighDPI200
that tests that the drag widget is moved properly on different scales)
are subject to move to DesktopDragDropClientOzone tests.

Note: a better place for these tests would be perhaps x11_unittests but
they cannot be moved there easily.  We keep that in mind and will try to
address that later.

Bug: 990756
Change-Id: If1905b2abace0db72a6aa8e4d0f0834bdafded81
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2250104
Commit-Queue: Alexander Dunaev <adunaev@igalia.com>
Reviewed-by: default avatarThomas Anderson <thomasanderson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#780400}
parent 5ef24014
...@@ -1277,6 +1277,7 @@ test("views_unittests") { ...@@ -1277,6 +1277,7 @@ test("views_unittests") {
sources += [ sources += [
"widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc", "widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc",
"widget/desktop_aura/desktop_screen_x11_unittest.cc", "widget/desktop_aura/desktop_screen_x11_unittest.cc",
"widget/desktop_aura/x11_drag_drop_client_unittest.cc",
] ]
deps += [ "//ui/base/x:test_support" ] deps += [ "//ui/base/x:test_support" ]
} }
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h"
#include <map> #include <map>
#include <memory> #include <memory>
#include <utility> #include <utility>
...@@ -27,11 +29,13 @@ ...@@ -27,11 +29,13 @@
#include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/x11_atom_cache.h"
#include "ui/gfx/x/x11_types.h" #include "ui/gfx/x/x11_types.h"
#include "ui/views/test/views_test_base.h" #include "ui/views/test/views_test_base.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_cursor_manager.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
// TODO(crbug.com/990756): Transfer all tests from this file to better places
// when DDDClientAuraX11 goes away.
namespace views { namespace views {
namespace { namespace {
...@@ -403,106 +407,6 @@ class DesktopDragDropClientAuraX11Test : public ViewsTestBase { ...@@ -403,106 +407,6 @@ class DesktopDragDropClientAuraX11Test : public ViewsTestBase {
DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11Test); DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11Test);
}; };
namespace {
void BasicStep2(TestDragDropClient* client, x11::Window 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<x11::Window>(events[0].data.l[0]));
EXPECT_EQ(1, events[0].data.l[1] & 1);
std::vector<x11::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<x11::Window>(events[0].data.l[0]));
const int kCoords =
TestDragDropClient::kMouseMoveX << 16 | TestDragDropClient::kMouseMoveY;
EXPECT_EQ(kCoords, events[1].data.l[2]);
EXPECT_EQ(client->GetAtom("XdndActionCopy"),
static_cast<x11::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<x11::Window>(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, x11::Window 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) {
x11::Window toplevel = static_cast<x11::Window>(1);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&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::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&BasicStep3, client(), toplevel));
result = StartDragAndDrop();
EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
}
void HighDPIStep(TestDragDropClient* client) { void HighDPIStep(TestDragDropClient* client) {
float scale = float scale =
display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor(); display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
...@@ -524,6 +428,8 @@ void HighDPIStep(TestDragDropClient* client) { ...@@ -524,6 +428,8 @@ void HighDPIStep(TestDragDropClient* client) {
client->OnMouseReleased(); client->OnMouseReleased();
} }
// TODO(crbug.com/990756): Turn this into tests of DesktopDragDropClientOzone
// or its equivalent.
TEST_F(DesktopDragDropClientAuraX11Test, HighDPI200) { TEST_F(DesktopDragDropClientAuraX11Test, HighDPI200) {
aura::TestScreen* screen = aura::TestScreen* screen =
static_cast<aura::TestScreen*>(display::Screen::GetScreen()); static_cast<aura::TestScreen*>(display::Screen::GetScreen());
...@@ -535,6 +441,8 @@ TEST_F(DesktopDragDropClientAuraX11Test, HighDPI200) { ...@@ -535,6 +441,8 @@ TEST_F(DesktopDragDropClientAuraX11Test, HighDPI200) {
EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result);
} }
// TODO(crbug.com/990756): Turn this into tests of DesktopDragDropClientOzone
// or its equivalent.
TEST_F(DesktopDragDropClientAuraX11Test, HighDPI150) { TEST_F(DesktopDragDropClientAuraX11Test, HighDPI150) {
aura::TestScreen* screen = aura::TestScreen* screen =
static_cast<aura::TestScreen*>(display::Screen::GetScreen()); static_cast<aura::TestScreen*>(display::Screen::GetScreen());
...@@ -548,212 +456,6 @@ TEST_F(DesktopDragDropClientAuraX11Test, HighDPI150) { ...@@ -548,212 +456,6 @@ TEST_F(DesktopDragDropClientAuraX11Test, HighDPI150) {
namespace { namespace {
void TargetDoesNotRespondStep2(TestDragDropClient* client) {
EXPECT_TRUE(client->IsMoveLoopRunning());
x11::Window toplevel = static_cast<x11::Window>(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::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&TargetDoesNotRespondStep2, client()));
int result = StartDragAndDrop();
EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result);
}
namespace {
void QueuePositionStep2(TestDragDropClient* client) {
EXPECT_TRUE(client->IsMoveLoopRunning());
x11::Window toplevel = static_cast<x11::Window>(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::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&QueuePositionStep2, client()));
int result = StartDragAndDrop();
EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
}
namespace {
void TargetChangesStep2(TestDragDropClient* client) {
EXPECT_TRUE(client->IsMoveLoopRunning());
x11::Window toplevel1 = static_cast<x11::Window>(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"));
x11::Window toplevel2 = static_cast<x11::Window>(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::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&TargetChangesStep2, client()));
int result = StartDragAndDrop();
EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
}
namespace {
void RejectAfterMouseReleaseStep2(TestDragDropClient* client) {
EXPECT_TRUE(client->IsMoveLoopRunning());
x11::Window toplevel = static_cast<x11::Window>(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, x11::Atom::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());
x11::Window toplevel = static_cast<x11::Window>(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, x11::Atom::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::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&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::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&RejectAfterMouseReleaseStep3, client()));
result = StartDragAndDrop();
EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result);
}
namespace {
// DragDropDelegate which counts the number of each type of drag-drop event and // DragDropDelegate which counts the number of each type of drag-drop event and
// keeps track of the most recent drag-drop event. // keeps track of the most recent drag-drop event.
class TestDragDropDelegate : public aura::client::DragDropDelegate { class TestDragDropDelegate : public aura::client::DragDropDelegate {
...@@ -932,6 +634,8 @@ void ChromeSourceTargetStep2(SimpleTestDragDropClient* client, ...@@ -932,6 +634,8 @@ void ChromeSourceTargetStep2(SimpleTestDragDropClient* client,
} // namespace } // namespace
// TODO(crbug.com/990756): Turn this into tests of DesktopDragDropClientOzone
// or its equivalent.
TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest, Basic) { TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest, Basic) {
base::ThreadTaskRunnerHandle::Get()->PostTask( base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, FROM_HERE,
...@@ -940,6 +644,8 @@ TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest, Basic) { ...@@ -940,6 +644,8 @@ TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest, Basic) {
EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
} }
// TODO(crbug.com/990756): Turn this into tests of DesktopDragDropClientOzone
// or its equivalent.
// Test that if 'Ctrl' is pressed during a drag and drop operation, that // Test that if 'Ctrl' is pressed during a drag and drop operation, that
// the aura::client::DragDropDelegate is properly notified. // the aura::client::DragDropDelegate is properly notified.
TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest, CtrlPressed) { TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest, CtrlPressed) {
......
// 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 "ui/base/x/x11_drag_drop_client.h"
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/aura/client/drag_drop_client.h"
#include "ui/aura/client/drag_drop_delegate.h"
#include "ui/aura/test/test_screen.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_move_loop.h"
#include "ui/base/x/x11_move_loop_delegate.h"
#include "ui/base/x/x11_os_exchange_data_provider.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/event_utils.h"
#include "ui/gfx/x/x11.h"
#include "ui/gfx/x/x11_atom_cache.h"
#include "ui/gfx/x/x11_types.h"
#include "ui/views/test/views_test_base.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 {
class TestDragDropClient;
// Collects messages which would otherwise be sent to |window_| via
// SendXClientEvent().
class ClientMessageEventCollector {
public:
ClientMessageEventCollector(x11::Window window, 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:
x11::Window window_;
// Not owned.
TestDragDropClient* client_;
std::vector<XClientMessageEvent> events_;
DISALLOW_COPY_AND_ASSIGN(ClientMessageEventCollector);
};
// An implementation of ui::X11MoveLoop where RunMoveLoop() always starts the
// move loop.
class TestMoveLoop : public ui::X11MoveLoop {
public:
explicit TestMoveLoop(ui::X11MoveLoopDelegate* delegate);
~TestMoveLoop() override;
// Returns true if the move loop is running.
bool IsRunning() const;
// ui::X11MoveLoop:
bool RunMoveLoop(bool can_grab_pointer,
::Cursor old_cursor,
::Cursor new_cursor) override;
void UpdateCursor(::Cursor cursor) override;
void EndMoveLoop() override;
private:
// Not owned.
ui::X11MoveLoopDelegate* delegate_;
// Ends the move loop.
base::OnceClosure quit_closure_;
bool is_running_ = false;
};
// Implementation of XDragDropClient which short circuits FindWindowFor().
class SimpleTestDragDropClient : public aura::client::DragDropClient,
public ui::XDragDropClient,
public ui::XDragDropClient::Delegate,
public ui::X11MoveLoopDelegate {
public:
SimpleTestDragDropClient(aura::Window*,
DesktopNativeCursorManager* cursor_manager);
~SimpleTestDragDropClient() override;
// Sets |window| as the topmost window for all mouse positions.
void SetTopmostXWindow(x11::Window window);
// Returns true if the move loop is running.
bool IsMoveLoopRunning();
// aura::client::DragDropClient:
int StartDragAndDrop(std::unique_ptr<ui::OSExchangeData> data,
aura::Window* root_window,
aura::Window* source_window,
const gfx::Point& screen_location,
int operation,
ui::DragDropTypes::DragEventSource source) override;
void DragCancel() override;
bool IsDragDropInProgress() override;
void AddObserver(aura::client::DragDropClientObserver* observer) override;
void RemoveObserver(aura::client::DragDropClientObserver* observer) override;
private:
// ui::XDragDropClient::Delegate:
std::unique_ptr<ui::XTopmostWindowFinder> CreateWindowFinder() override;
int UpdateDrag(const gfx::Point& screen_point) override;
void UpdateCursor(
ui::DragDropTypes::DragOperation negotiated_operation) override;
void OnBeginForeignDrag(x11::Window window) override;
void OnEndForeignDrag() override;
void OnBeforeDragLeave() override;
int PerformDrop() override;
void EndDragLoop() override;
// XDragDropClient:
x11::Window FindWindowFor(const gfx::Point& screen_point) override;
// ui::X11MoveLoopDelegate:
void OnMouseMovement(const gfx::Point& screen_point,
int flags,
base::TimeTicks event_time) override;
void OnMouseReleased() override;
void OnMoveLoopEnded() override;
std::unique_ptr<ui::X11MoveLoop> CreateMoveLoop(
ui::X11MoveLoopDelegate* delegate);
// The x11::Window of the window which is simulated to be the topmost window.
x11::Window target_window_ = x11::Window::None;
// The move loop. Not owned.
TestMoveLoop* loop_ = nullptr;
base::OnceClosure quit_closure_;
DISALLOW_COPY_AND_ASSIGN(SimpleTestDragDropClient);
};
// Implementation of XDragDropClient which works with a fake
// |XDragDropClient::source_current_window_|.
class TestDragDropClient : public SimpleTestDragDropClient {
public:
// The location in screen coordinates used for the synthetic mouse moves
// generated in SetTopmostXWindowAndMoveMouse().
static constexpr int kMouseMoveX = 100;
static constexpr int kMouseMoveY = 200;
TestDragDropClient(aura::Window* window,
DesktopNativeCursorManager* cursor_manager);
~TestDragDropClient() override;
// Returns the x11::Window of the window which initiated the drag.
x11::Window source_xwindow() { return source_window_; }
// Returns the Atom with |name|.
x11::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(x11::Window window,
ClientMessageEventCollector* collector);
// Builds an XdndStatus message and sends it to
// XDragDropClient.
void OnStatus(x11::Window target_window,
bool will_accept_drop,
x11::Atom accepted_action);
// Builds an XdndFinished message and sends it to
// XDragDropClient.
void OnFinished(x11::Window target_window,
bool accepted_drop,
x11::Atom performed_action);
// Sets |window| as the topmost window at the current mouse position and
// generates a synthetic mouse move.
void SetTopmostXWindowAndMoveMouse(x11::Window window);
private:
// XDragDropClient:
void SendXClientEvent(x11::Window window, XEvent* event) override;
// The x11::Window of the window which initiated the drag.
x11::Window source_window_;
// Map of x11::Windows to the collector which intercepts XClientMessageEvents
// for that window.
std::map<x11::Window, ClientMessageEventCollector*> collectors_;
DISALLOW_COPY_AND_ASSIGN(TestDragDropClient);
};
///////////////////////////////////////////////////////////////////////////////
// ClientMessageEventCollector
ClientMessageEventCollector::ClientMessageEventCollector(
x11::Window window,
TestDragDropClient* client)
: window_(window), client_(client) {
client->SetEventCollectorFor(window, this);
}
ClientMessageEventCollector::~ClientMessageEventCollector() {
client_->SetEventCollectorFor(window_, nullptr);
}
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);
}
///////////////////////////////////////////////////////////////////////////////
// TestMoveLoop
TestMoveLoop::TestMoveLoop(ui::X11MoveLoopDelegate* delegate)
: delegate_(delegate) {}
TestMoveLoop::~TestMoveLoop() = default;
bool TestMoveLoop::IsRunning() const {
return is_running_;
}
bool TestMoveLoop::RunMoveLoop(bool can_grab_pointer,
::Cursor old_cursor,
::Cursor new_cursor) {
is_running_ = true;
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
return true;
}
void TestMoveLoop::UpdateCursor(::Cursor cursor) {}
void TestMoveLoop::EndMoveLoop() {
if (is_running_) {
delegate_->OnMoveLoopEnded();
is_running_ = false;
std::move(quit_closure_).Run();
}
}
///////////////////////////////////////////////////////////////////////////////
// SimpleTestDragDropClient
SimpleTestDragDropClient::SimpleTestDragDropClient(
aura::Window* window,
DesktopNativeCursorManager* cursor_manager)
: ui::XDragDropClient(this,
gfx::GetXDisplay(),
window->GetHost()->GetAcceleratedWidget()) {}
SimpleTestDragDropClient::~SimpleTestDragDropClient() = default;
void SimpleTestDragDropClient::SetTopmostXWindow(x11::Window window) {
target_window_ = window;
}
bool SimpleTestDragDropClient::IsMoveLoopRunning() {
return loop_->IsRunning();
}
std::unique_ptr<ui::X11MoveLoop> SimpleTestDragDropClient::CreateMoveLoop(
ui::X11MoveLoopDelegate* delegate) {
loop_ = new TestMoveLoop(delegate);
return base::WrapUnique(loop_);
}
int SimpleTestDragDropClient::StartDragAndDrop(
std::unique_ptr<ui::OSExchangeData> data,
aura::Window* root_window,
aura::Window* source_window,
const gfx::Point& screen_location,
int operation,
ui::DragDropTypes::DragEventSource source) {
InitDrag(operation, data.get());
auto loop = CreateMoveLoop(this);
// Windows has a specific method, DoDragDrop(), which performs the entire
// drag. We have to emulate this, so we spin off a nested runloop which will
// track all cursor movement and reroute events to a specific handler.
auto cursor_manager_ = std::make_unique<DesktopNativeCursorManager>();
loop_->RunMoveLoop(
!source_window->HasCapture(),
source_window->GetHost()->last_cursor().platform(),
cursor_manager_->GetInitializedCursor(ui::mojom::CursorType::kGrabbing)
.platform());
auto resulting_operation = negotiated_operation();
CleanupDrag();
return resulting_operation;
}
void SimpleTestDragDropClient::DragCancel() {}
bool SimpleTestDragDropClient::IsDragDropInProgress() {
return false;
}
void SimpleTestDragDropClient::AddObserver(
aura::client::DragDropClientObserver* observer) {}
void SimpleTestDragDropClient::RemoveObserver(
aura::client::DragDropClientObserver* observer) {}
int SimpleTestDragDropClient::UpdateDrag(const gfx::Point& screen_point) {
return 0;
}
std::unique_ptr<ui::XTopmostWindowFinder>
SimpleTestDragDropClient::CreateWindowFinder() {
return {};
}
void SimpleTestDragDropClient::UpdateCursor(
ui::DragDropTypes::DragOperation negotiated_operation) {}
void SimpleTestDragDropClient::OnBeginForeignDrag(x11::Window window) {}
void SimpleTestDragDropClient::OnEndForeignDrag() {}
void SimpleTestDragDropClient::OnBeforeDragLeave() {}
int SimpleTestDragDropClient::PerformDrop() {
return 0;
}
void SimpleTestDragDropClient::EndDragLoop() {
// std::move(quit_closure_).Run();
loop_->EndMoveLoop();
}
x11::Window SimpleTestDragDropClient::FindWindowFor(
const gfx::Point& screen_point) {
return target_window_;
}
void SimpleTestDragDropClient::OnMouseMovement(const gfx::Point& screen_point,
int flags,
base::TimeTicks event_time) {
HandleMouseMovement(screen_point, flags, event_time);
}
void SimpleTestDragDropClient::OnMouseReleased() {
HandleMouseReleased();
}
void SimpleTestDragDropClient::OnMoveLoopEnded() {
HandleMoveLoopEnded();
}
///////////////////////////////////////////////////////////////////////////////
// TestDragDropClient
TestDragDropClient::TestDragDropClient(
aura::Window* window,
DesktopNativeCursorManager* cursor_manager)
: SimpleTestDragDropClient(window, cursor_manager),
source_window_(window->GetHost()->GetAcceleratedWidget()) {}
TestDragDropClient::~TestDragDropClient() = default;
x11::Atom TestDragDropClient::GetAtom(const char* name) {
return gfx::GetAtom(name);
}
bool TestDragDropClient::MessageHasType(const XClientMessageEvent& event,
const char* type) {
return static_cast<x11::Atom>(event.message_type) == GetAtom(type);
}
void TestDragDropClient::SetEventCollectorFor(
x11::Window window,
ClientMessageEventCollector* collector) {
if (collector)
collectors_[window] = collector;
else
collectors_.erase(window);
}
void TestDragDropClient::OnStatus(x11::Window target_window,
bool will_accept_drop,
x11::Atom accepted_action) {
XClientMessageEvent event;
event.message_type = static_cast<uint32_t>(GetAtom("XdndStatus"));
event.format = 32;
event.window = static_cast<uint32_t>(source_window_);
event.data.l[0] = static_cast<uint32_t>(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] = static_cast<uint32_t>(accepted_action);
HandleXdndEvent(event);
}
void TestDragDropClient::OnFinished(x11::Window target_window,
bool accepted_drop,
x11::Atom performed_action) {
XClientMessageEvent event;
event.message_type = static_cast<uint32_t>(GetAtom("XdndFinished"));
event.format = 32;
event.window = static_cast<uint32_t>(source_window_);
event.data.l[0] = static_cast<uint32_t>(target_window);
event.data.l[1] = accepted_drop ? 1 : 0;
event.data.l[2] = static_cast<uint32_t>(performed_action);
event.data.l[3] = 0;
event.data.l[4] = 0;
HandleXdndEvent(event);
}
void TestDragDropClient::SetTopmostXWindowAndMoveMouse(x11::Window window) {
SetTopmostXWindow(window);
HandleMouseMovement(gfx::Point(kMouseMoveX, kMouseMoveY), ui::EF_NONE,
ui::EventTimeForNow());
}
void TestDragDropClient::SendXClientEvent(x11::Window window, XEvent* event) {
auto it = collectors_.find(window);
if (it != collectors_.end())
it->second->RecordEvent(event->xclient);
}
} // namespace
class X11DragDropClientTest : public ViewsTestBase {
public:
X11DragDropClientTest() = default;
~X11DragDropClientTest() override = default;
int StartDragAndDrop() {
auto data(std::make_unique<ui::OSExchangeData>());
data->SetString(base::ASCIIToUTF16("Test"));
SkBitmap drag_bitmap;
drag_bitmap.allocN32Pixels(10, 10);
drag_bitmap.eraseARGB(0xFF, 0, 0, 0);
gfx::ImageSkia drag_image(gfx::ImageSkia::CreateFrom1xBitmap(drag_bitmap));
data->provider().SetDragImage(drag_image, gfx::Vector2d());
return client_->StartDragAndDrop(
std::move(data), widget_->GetNativeWindow()->GetRootWindow(),
widget_->GetNativeWindow(), gfx::Point(), ui::DragDropTypes::DRAG_COPY,
ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
}
// ViewsTestBase:
void SetUp() override {
set_native_widget_type(NativeWidgetType::kDesktop);
ViewsTestBase::SetUp();
// Create widget to initiate the drags.
widget_ = std::make_unique<Widget>();
Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.bounds = gfx::Rect(100, 100);
widget_->Init(std::move(params));
widget_->Show();
cursor_manager_ = std::make_unique<DesktopNativeCursorManager>();
client_ = std::make_unique<TestDragDropClient>(widget_->GetNativeWindow(),
cursor_manager_.get());
// client_->Init();
}
void TearDown() override {
client_.reset();
cursor_manager_.reset();
widget_.reset();
ViewsTestBase::TearDown();
}
TestDragDropClient* client() { return client_.get(); }
private:
std::unique_ptr<TestDragDropClient> client_;
std::unique_ptr<DesktopNativeCursorManager> cursor_manager_;
// The widget used to initiate drags.
std::unique_ptr<Widget> widget_;
DISALLOW_COPY_AND_ASSIGN(X11DragDropClientTest);
};
namespace {
void BasicStep2(TestDragDropClient* client, x11::Window 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<x11::Window>(events[0].data.l[0]));
EXPECT_EQ(1, events[0].data.l[1] & 1);
std::vector<x11::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<x11::Window>(events[0].data.l[0]));
const int kCoords =
TestDragDropClient::kMouseMoveX << 16 | TestDragDropClient::kMouseMoveY;
EXPECT_EQ(kCoords, events[1].data.l[2]);
EXPECT_EQ(client->GetAtom("XdndActionCopy"),
static_cast<x11::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->HandleMouseReleased();
events = collector.PopAllEvents();
ASSERT_EQ(1u, events.size());
EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop"));
EXPECT_EQ(client->source_xwindow(),
static_cast<x11::Window>(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, x11::Window 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->HandleMouseReleased();
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(X11DragDropClientTest, Basic) {
x11::Window toplevel = static_cast<x11::Window>(1);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&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::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&BasicStep3, client(), toplevel));
result = StartDragAndDrop();
EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
}
namespace {
void TargetDoesNotRespondStep2(TestDragDropClient* client) {
EXPECT_TRUE(client->IsMoveLoopRunning());
x11::Window toplevel = static_cast<x11::Window>(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->HandleMouseReleased();
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(X11DragDropClientTest, TargetDoesNotRespond) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&TargetDoesNotRespondStep2, client()));
int result = StartDragAndDrop();
EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result);
}
namespace {
void QueuePositionStep2(TestDragDropClient* client) {
EXPECT_TRUE(client->IsMoveLoopRunning());
x11::Window toplevel = static_cast<x11::Window>(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->HandleMouseReleased();
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(X11DragDropClientTest, QueuePosition) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&QueuePositionStep2, client()));
int result = StartDragAndDrop();
EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
}
namespace {
void TargetChangesStep2(TestDragDropClient* client) {
EXPECT_TRUE(client->IsMoveLoopRunning());
x11::Window toplevel1 = static_cast<x11::Window>(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"));
x11::Window toplevel2 = static_cast<x11::Window>(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->HandleMouseReleased();
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(X11DragDropClientTest, TargetChanges) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&TargetChangesStep2, client()));
int result = StartDragAndDrop();
EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
}
namespace {
void RejectAfterMouseReleaseStep2(TestDragDropClient* client) {
EXPECT_TRUE(client->IsMoveLoopRunning());
x11::Window toplevel = static_cast<x11::Window>(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->HandleMouseReleased();
// Reject the drop.
client->OnStatus(toplevel, false, x11::Atom::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());
x11::Window toplevel = static_cast<x11::Window>(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->HandleMouseReleased();
events = collector.PopAllEvents();
ASSERT_EQ(1u, events.size());
EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop"));
EXPECT_TRUE(client->IsMoveLoopRunning());
client->OnFinished(toplevel, false, x11::Atom::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(X11DragDropClientTest, RejectAfterMouseRelease) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&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::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&RejectAfterMouseReleaseStep3, client()));
result = StartDragAndDrop();
EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result);
}
} // namespace views
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment