1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 // Include views_test_base.h first because the definition of None in X.h
9 // conflicts with the definition of None in gtest-type-util.h
10 #include "ui/views/test/views_test_base.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/run_loop.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "ui/aura/window.h"
16 #include "ui/aura/window_tree_host.h"
17 #include "ui/base/dragdrop/os_exchange_data.h"
18 #include "ui/base/x/x11_util.h"
19 #include "ui/events/event_utils.h"
20 #include "ui/gfx/x/x11_atom_cache.h"
21 #include "ui/gfx/x/x11_types.h"
22 #include "ui/views/widget/desktop_aura/desktop_cursor_loader_updater.h"
23 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h"
24 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
25 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
26 #include "ui/views/widget/desktop_aura/x11_move_loop.h"
27 #include "ui/views/widget/widget.h"
28 #include "ui/wm/public/drag_drop_delegate.h"
36 const char* kAtomsToCache
[] = {
48 class TestDragDropClient
;
50 // Collects messages which would otherwise be sent to |xid_| via
51 // SendXClientEvent().
52 class ClientMessageEventCollector
{
54 ClientMessageEventCollector(::Window xid
, TestDragDropClient
* client
);
55 virtual ~ClientMessageEventCollector();
57 // Returns true if |events_| is non-empty.
58 bool HasEvents() const {
59 return !events_
.empty();
62 // Pops all of |events_| and returns the popped events in the order that they
64 std::vector
<XClientMessageEvent
> PopAllEvents();
66 // Adds |event| to the stack.
67 void RecordEvent(const XClientMessageEvent
& event
);
73 TestDragDropClient
* client_
;
75 std::vector
<XClientMessageEvent
> events_
;
77 DISALLOW_COPY_AND_ASSIGN(ClientMessageEventCollector
);
80 // An implementation of X11MoveLoop where RunMoveLoop() always starts the move
82 class TestMoveLoop
: public X11MoveLoop
{
84 explicit TestMoveLoop(X11MoveLoopDelegate
* delegate
);
85 ~TestMoveLoop() override
;
87 // Returns true if the move loop is running.
88 bool IsRunning() const;
91 bool RunMoveLoop(aura::Window
* window
, gfx::NativeCursor cursor
) override
;
92 void UpdateCursor(gfx::NativeCursor cursor
) override
;
93 void EndMoveLoop() override
;
97 X11MoveLoopDelegate
* delegate_
;
99 // Ends the move loop.
100 base::Closure quit_closure_
;
105 // Implementation of DesktopDragDropClientAuraX11 which short circuits
107 class SimpleTestDragDropClient
: public DesktopDragDropClientAuraX11
{
109 SimpleTestDragDropClient(aura::Window
*,
110 DesktopNativeCursorManager
* cursor_manager
);
111 ~SimpleTestDragDropClient() override
;
113 // Sets |xid| as the topmost window for all mouse positions.
114 void SetTopmostXWindow(XID xid
);
116 // Returns true if the move loop is running.
117 bool IsMoveLoopRunning();
120 // DesktopDragDropClientAuraX11:
121 scoped_ptr
<X11MoveLoop
> CreateMoveLoop(
122 X11MoveLoopDelegate
* delegate
) override
;
123 XID
FindWindowFor(const gfx::Point
& screen_point
) override
;
125 // The XID of the window which is simulated to be the topmost window.
128 // The move loop. Not owned.
131 DISALLOW_COPY_AND_ASSIGN(SimpleTestDragDropClient
);
134 // Implementation of DesktopDragDropClientAuraX11 which works with a fake
135 // |DesktopDragDropClientAuraX11::source_current_window_|.
136 class TestDragDropClient
: public SimpleTestDragDropClient
{
138 // The location in screen coordinates used for the synthetic mouse moves
139 // generated in SetTopmostXWindowAndMoveMouse().
140 static const int kMouseMoveX
;
141 static const int kMouseMoveY
;
143 TestDragDropClient(aura::Window
* window
,
144 DesktopNativeCursorManager
* cursor_manager
);
145 ~TestDragDropClient() override
;
147 // Returns the XID of the window which initiated the drag.
148 ::Window
source_xwindow() {
152 // Returns the Atom with |name|.
153 Atom
GetAtom(const char* name
);
155 // Returns true if the event's message has |type|.
156 bool MessageHasType(const XClientMessageEvent
& event
,
159 // Sets |collector| to collect XClientMessageEvents which would otherwise
160 // have been sent to the drop target window.
161 void SetEventCollectorFor(::Window xid
,
162 ClientMessageEventCollector
* collector
);
164 // Builds an XdndStatus message and sends it to
165 // DesktopDragDropClientAuraX11.
166 void OnStatus(XID target_window
,
167 bool will_accept_drop
,
168 ::Atom accepted_action
);
170 // Builds an XdndFinished message and sends it to
171 // DesktopDragDropClientAuraX11.
172 void OnFinished(XID target_window
,
174 ::Atom performed_action
);
176 // Sets |xid| as the topmost window at the current mouse position and
177 // generates a synthetic mouse move.
178 void SetTopmostXWindowAndMoveMouse(::Window xid
);
181 // DesktopDragDropClientAuraX11:
182 void SendXClientEvent(::Window xid
, XEvent
* event
) override
;
184 // The XID of the window which initiated the drag.
185 ::Window source_xid_
;
187 // The move loop. Not owned.
190 // Map of ::Windows to the collector which intercepts XClientMessageEvents
192 std::map
< ::Window
, ClientMessageEventCollector
*> collectors_
;
194 ui::X11AtomCache atom_cache_
;
196 DISALLOW_COPY_AND_ASSIGN(TestDragDropClient
);
199 ///////////////////////////////////////////////////////////////////////////////
200 // ClientMessageEventCollector
202 ClientMessageEventCollector::ClientMessageEventCollector(
204 TestDragDropClient
* client
)
207 client
->SetEventCollectorFor(xid
, this);
210 ClientMessageEventCollector::~ClientMessageEventCollector() {
211 client_
->SetEventCollectorFor(xid_
, NULL
);
214 std::vector
<XClientMessageEvent
> ClientMessageEventCollector::PopAllEvents() {
215 std::vector
<XClientMessageEvent
> to_return
;
216 to_return
.swap(events_
);
220 void ClientMessageEventCollector::RecordEvent(
221 const XClientMessageEvent
& event
) {
222 events_
.push_back(event
);
225 ///////////////////////////////////////////////////////////////////////////////
228 TestMoveLoop::TestMoveLoop(X11MoveLoopDelegate
* delegate
)
229 : delegate_(delegate
),
233 TestMoveLoop::~TestMoveLoop() {
236 bool TestMoveLoop::IsRunning() const {
240 bool TestMoveLoop::RunMoveLoop(
241 aura::Window
* window
,
242 gfx::NativeCursor cursor
) {
244 base::RunLoop run_loop
;
245 quit_closure_
= run_loop
.QuitClosure();
250 void TestMoveLoop::UpdateCursor(gfx::NativeCursor cursor
) {
253 void TestMoveLoop::EndMoveLoop() {
255 delegate_
->OnMoveLoopEnded();
261 ///////////////////////////////////////////////////////////////////////////////
262 // SimpleTestDragDropClient
264 SimpleTestDragDropClient::SimpleTestDragDropClient(
265 aura::Window
* window
,
266 DesktopNativeCursorManager
* cursor_manager
)
267 : DesktopDragDropClientAuraX11(window
,
270 window
->GetHost()->GetAcceleratedWidget()),
275 SimpleTestDragDropClient::~SimpleTestDragDropClient() {
278 void SimpleTestDragDropClient::SetTopmostXWindow(XID xid
) {
282 bool SimpleTestDragDropClient::IsMoveLoopRunning() {
283 return loop_
->IsRunning();
286 scoped_ptr
<X11MoveLoop
> SimpleTestDragDropClient::CreateMoveLoop(
287 X11MoveLoopDelegate
* delegate
) {
288 loop_
= new TestMoveLoop(delegate
);
289 return scoped_ptr
<X11MoveLoop
>(loop_
);
292 XID
SimpleTestDragDropClient::FindWindowFor(const gfx::Point
& screen_point
) {
296 ///////////////////////////////////////////////////////////////////////////////
297 // TestDragDropClient
300 const int TestDragDropClient::kMouseMoveX
= 100;
303 const int TestDragDropClient::kMouseMoveY
= 200;
305 TestDragDropClient::TestDragDropClient(
306 aura::Window
* window
,
307 DesktopNativeCursorManager
* cursor_manager
)
308 : SimpleTestDragDropClient(window
, cursor_manager
),
309 source_xid_(window
->GetHost()->GetAcceleratedWidget()),
311 atom_cache_(gfx::GetXDisplay(), kAtomsToCache
) {
314 TestDragDropClient::~TestDragDropClient() {
317 Atom
TestDragDropClient::GetAtom(const char* name
) {
318 return atom_cache_
.GetAtom(name
);
321 bool TestDragDropClient::MessageHasType(const XClientMessageEvent
& event
,
323 return event
.message_type
== atom_cache_
.GetAtom(type
);
326 void TestDragDropClient::SetEventCollectorFor(
328 ClientMessageEventCollector
* collector
) {
330 collectors_
[xid
] = collector
;
332 collectors_
.erase(xid
);
335 void TestDragDropClient::OnStatus(XID target_window
,
336 bool will_accept_drop
,
337 ::Atom accepted_action
) {
338 XClientMessageEvent event
;
339 event
.message_type
= atom_cache_
.GetAtom("XdndStatus");
341 event
.window
= source_xid_
;
342 event
.data
.l
[0] = target_window
;
343 event
.data
.l
[1] = will_accept_drop
? 1 : 0;
346 event
.data
.l
[4] = accepted_action
;
350 void TestDragDropClient::OnFinished(XID target_window
,
352 ::Atom performed_action
) {
353 XClientMessageEvent event
;
354 event
.message_type
= atom_cache_
.GetAtom("XdndFinished");
356 event
.window
= source_xid_
;
357 event
.data
.l
[0] = target_window
;
358 event
.data
.l
[1] = accepted_drop
? 1 : 0;
359 event
.data
.l
[2] = performed_action
;
362 OnXdndFinished(event
);
365 void TestDragDropClient::SetTopmostXWindowAndMoveMouse(::Window xid
) {
366 SetTopmostXWindow(xid
);
367 OnMouseMovement(gfx::Point(kMouseMoveX
, kMouseMoveY
), ui::EF_NONE
,
368 ui::EventTimeForNow());
371 void TestDragDropClient::SendXClientEvent(::Window xid
, XEvent
* event
) {
372 std::map
< ::Window
, ClientMessageEventCollector
*>::iterator it
=
373 collectors_
.find(xid
);
374 if (it
!= collectors_
.end())
375 it
->second
->RecordEvent(event
->xclient
);
380 class DesktopDragDropClientAuraX11Test
: public ViewsTestBase
{
382 DesktopDragDropClientAuraX11Test() {
385 ~DesktopDragDropClientAuraX11Test() override
{}
387 int StartDragAndDrop() {
388 ui::OSExchangeData data
;
389 data
.SetString(base::ASCIIToUTF16("Test"));
391 return client_
->StartDragAndDrop(
393 widget_
->GetNativeWindow()->GetRootWindow(),
394 widget_
->GetNativeWindow(),
396 ui::DragDropTypes::DRAG_COPY
,
397 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE
);
401 void SetUp() override
{
402 ViewsTestBase::SetUp();
404 // Create widget to initiate the drags.
405 widget_
.reset(new Widget
);
406 Widget::InitParams
params(Widget::InitParams::TYPE_WINDOW
);
407 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
408 params
.native_widget
= new DesktopNativeWidgetAura(widget_
.get());
409 params
.bounds
= gfx::Rect(100, 100);
410 widget_
->Init(params
);
413 cursor_manager_
.reset(new DesktopNativeCursorManager(
414 DesktopCursorLoaderUpdater::Create()));
416 client_
.reset(new TestDragDropClient(widget_
->GetNativeWindow(),
417 cursor_manager_
.get()));
421 void TearDown() override
{
423 cursor_manager_
.reset();
425 ViewsTestBase::TearDown();
428 TestDragDropClient
* client() {
429 return client_
.get();
433 scoped_ptr
<TestDragDropClient
> client_
;
434 scoped_ptr
<DesktopNativeCursorManager
> cursor_manager_
;
436 // The widget used to initiate drags.
437 scoped_ptr
<Widget
> widget_
;
439 DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11Test
);
444 void BasicStep2(TestDragDropClient
* client
, XID toplevel
) {
445 EXPECT_TRUE(client
->IsMoveLoopRunning());
447 ClientMessageEventCollector
collector(toplevel
, client
);
448 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
450 // XdndEnter should have been sent to |toplevel| before the XdndPosition
452 std::vector
<XClientMessageEvent
> events
= collector
.PopAllEvents();
453 ASSERT_EQ(2u, events
.size());
455 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndEnter"));
456 EXPECT_EQ(client
->source_xwindow(),
457 static_cast<XID
>(events
[0].data
.l
[0]));
458 EXPECT_EQ(1, events
[0].data
.l
[1] & 1);
459 std::vector
<Atom
> targets
;
460 ui::GetAtomArrayProperty(client
->source_xwindow(), "XdndTypeList", &targets
);
461 EXPECT_FALSE(targets
.empty());
463 EXPECT_TRUE(client
->MessageHasType(events
[1], "XdndPosition"));
464 EXPECT_EQ(client
->source_xwindow(),
465 static_cast<XID
>(events
[0].data
.l
[0]));
467 TestDragDropClient::kMouseMoveX
<< 16 | TestDragDropClient::kMouseMoveY
;
468 EXPECT_EQ(kCoords
, events
[1].data
.l
[2]);
469 EXPECT_EQ(client
->GetAtom("XdndActionCopy"),
470 static_cast<Atom
>(events
[1].data
.l
[4]));
472 client
->OnStatus(toplevel
, true, client
->GetAtom("XdndActionCopy"));
474 // Because there is no unprocessed XdndPosition, the drag drop client should
475 // send XdndDrop immediately after the mouse is released.
476 client
->OnMouseReleased();
478 events
= collector
.PopAllEvents();
479 ASSERT_EQ(1u, events
.size());
480 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndDrop"));
481 EXPECT_EQ(client
->source_xwindow(),
482 static_cast<XID
>(events
[0].data
.l
[0]));
484 // Send XdndFinished to indicate that the drag drop client can cleanup any
485 // data related to this drag. The move loop should end only after the
486 // XdndFinished message was received.
487 EXPECT_TRUE(client
->IsMoveLoopRunning());
488 client
->OnFinished(toplevel
, true, client
->GetAtom("XdndActionCopy"));
489 EXPECT_FALSE(client
->IsMoveLoopRunning());
492 void BasicStep3(TestDragDropClient
* client
, XID toplevel
) {
493 EXPECT_TRUE(client
->IsMoveLoopRunning());
495 ClientMessageEventCollector
collector(toplevel
, client
);
496 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
498 std::vector
<XClientMessageEvent
> events
= collector
.PopAllEvents();
499 ASSERT_EQ(2u, events
.size());
500 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndEnter"));
501 EXPECT_TRUE(client
->MessageHasType(events
[1], "XdndPosition"));
503 client
->OnStatus(toplevel
, true, client
->GetAtom("XdndActionCopy"));
504 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
505 events
= collector
.PopAllEvents();
506 ASSERT_EQ(1u, events
.size());
507 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndPosition"));
509 // We have not received an XdndStatus ack for the second XdndPosition message.
510 // Test that sending XdndDrop is delayed till the XdndStatus ack is received.
511 client
->OnMouseReleased();
512 EXPECT_FALSE(collector
.HasEvents());
514 client
->OnStatus(toplevel
, true, client
->GetAtom("XdndActionCopy"));
515 events
= collector
.PopAllEvents();
516 ASSERT_EQ(1u, events
.size());
517 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndDrop"));
519 EXPECT_TRUE(client
->IsMoveLoopRunning());
520 client
->OnFinished(toplevel
, true, client
->GetAtom("XdndActionCopy"));
521 EXPECT_FALSE(client
->IsMoveLoopRunning());
526 TEST_F(DesktopDragDropClientAuraX11Test
, Basic
) {
529 base::MessageLoop::current()->PostTask(FROM_HERE
,
530 base::Bind(&BasicStep2
,
533 int result
= StartDragAndDrop();
534 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY
, result
);
536 // Do another drag and drop to test that the data is properly cleaned up as a
537 // result of the XdndFinished message.
538 base::MessageLoop::current()->PostTask(FROM_HERE
,
539 base::Bind(&BasicStep3
,
542 result
= StartDragAndDrop();
543 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY
, result
);
548 void TargetDoesNotRespondStep2(TestDragDropClient
* client
) {
549 EXPECT_TRUE(client
->IsMoveLoopRunning());
552 ClientMessageEventCollector
collector(toplevel
, client
);
553 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
555 std::vector
<XClientMessageEvent
> events
= collector
.PopAllEvents();
556 ASSERT_EQ(2u, events
.size());
557 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndEnter"));
558 EXPECT_TRUE(client
->MessageHasType(events
[1], "XdndPosition"));
560 client
->OnMouseReleased();
561 events
= collector
.PopAllEvents();
562 ASSERT_EQ(1u, events
.size());
563 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndLeave"));
564 EXPECT_FALSE(client
->IsMoveLoopRunning());
569 // Test that we do not wait for the target to send XdndStatus if we have not
570 // received any XdndStatus messages at all from the target. The Unity
571 // DNDCollectionWindow is an example of an XdndAware target which does not
572 // respond to XdndPosition messages at all.
573 TEST_F(DesktopDragDropClientAuraX11Test
, TargetDoesNotRespond
) {
574 base::MessageLoop::current()->PostTask(
576 base::Bind(&TargetDoesNotRespondStep2
, client()));
577 int result
= StartDragAndDrop();
578 EXPECT_EQ(ui::DragDropTypes::DRAG_NONE
, result
);
583 void QueuePositionStep2(TestDragDropClient
* client
) {
584 EXPECT_TRUE(client
->IsMoveLoopRunning());
587 ClientMessageEventCollector
collector(toplevel
, client
);
588 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
589 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
590 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
592 std::vector
<XClientMessageEvent
> events
= collector
.PopAllEvents();
593 ASSERT_EQ(2u, events
.size());
594 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndEnter"));
595 EXPECT_TRUE(client
->MessageHasType(events
[1], "XdndPosition"));
597 client
->OnStatus(toplevel
, true, client
->GetAtom("XdndActionCopy"));
598 events
= collector
.PopAllEvents();
599 ASSERT_EQ(1u, events
.size());
600 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndPosition"));
602 client
->OnStatus(toplevel
, true, client
->GetAtom("XdndActionCopy"));
603 EXPECT_FALSE(collector
.HasEvents());
605 client
->OnMouseReleased();
606 events
= collector
.PopAllEvents();
607 ASSERT_EQ(1u, events
.size());
608 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndDrop"));
610 EXPECT_TRUE(client
->IsMoveLoopRunning());
611 client
->OnFinished(toplevel
, true, client
->GetAtom("XdndActionCopy"));
612 EXPECT_FALSE(client
->IsMoveLoopRunning());
617 // Test that XdndPosition messages are queued till the pending XdndPosition
618 // message is acked via an XdndStatus message.
619 TEST_F(DesktopDragDropClientAuraX11Test
, QueuePosition
) {
620 base::MessageLoop::current()->PostTask(
622 base::Bind(&QueuePositionStep2
, client()));
623 int result
= StartDragAndDrop();
624 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY
, result
);
629 void TargetChangesStep2(TestDragDropClient
* client
) {
630 EXPECT_TRUE(client
->IsMoveLoopRunning());
633 ClientMessageEventCollector
collector1(toplevel1
, client
);
634 client
->SetTopmostXWindowAndMoveMouse(toplevel1
);
636 std::vector
<XClientMessageEvent
> events1
= collector1
.PopAllEvents();
637 ASSERT_EQ(2u, events1
.size());
638 EXPECT_TRUE(client
->MessageHasType(events1
[0], "XdndEnter"));
639 EXPECT_TRUE(client
->MessageHasType(events1
[1], "XdndPosition"));
642 ClientMessageEventCollector
collector2(toplevel2
, client
);
643 client
->SetTopmostXWindowAndMoveMouse(toplevel2
);
645 // It is possible for |toplevel1| to send XdndStatus after the source has sent
646 // XdndLeave but before |toplevel1| has received the XdndLeave message. The
647 // XdndStatus message should be ignored.
648 client
->OnStatus(toplevel1
, true, client
->GetAtom("XdndActionCopy"));
649 events1
= collector1
.PopAllEvents();
650 ASSERT_EQ(1u, events1
.size());
651 EXPECT_TRUE(client
->MessageHasType(events1
[0], "XdndLeave"));
653 std::vector
<XClientMessageEvent
> events2
= collector2
.PopAllEvents();
654 ASSERT_EQ(2u, events2
.size());
655 EXPECT_TRUE(client
->MessageHasType(events2
[0], "XdndEnter"));
656 EXPECT_TRUE(client
->MessageHasType(events2
[1], "XdndPosition"));
658 client
->OnStatus(toplevel2
, true, client
->GetAtom("XdndActionCopy"));
659 client
->OnMouseReleased();
660 events2
= collector2
.PopAllEvents();
661 ASSERT_EQ(1u, events2
.size());
662 EXPECT_TRUE(client
->MessageHasType(events2
[0], "XdndDrop"));
664 EXPECT_TRUE(client
->IsMoveLoopRunning());
665 client
->OnFinished(toplevel2
, true, client
->GetAtom("XdndActionCopy"));
666 EXPECT_FALSE(client
->IsMoveLoopRunning());
671 // Test the behavior when the target changes during a drag.
672 TEST_F(DesktopDragDropClientAuraX11Test
, TargetChanges
) {
673 base::MessageLoop::current()->PostTask(
675 base::Bind(&TargetChangesStep2
, client()));
676 int result
= StartDragAndDrop();
677 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY
, result
);
682 void RejectAfterMouseReleaseStep2(TestDragDropClient
* client
) {
683 EXPECT_TRUE(client
->IsMoveLoopRunning());
686 ClientMessageEventCollector
collector(toplevel
, client
);
687 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
689 std::vector
<XClientMessageEvent
> events
= collector
.PopAllEvents();
690 ASSERT_EQ(2u, events
.size());
691 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndEnter"));
692 EXPECT_TRUE(client
->MessageHasType(events
[1], "XdndPosition"));
694 client
->OnStatus(toplevel
, true, client
->GetAtom("XdndActionCopy"));
695 EXPECT_FALSE(collector
.HasEvents());
697 // Send another mouse move such that there is a pending XdndPosition.
698 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
699 events
= collector
.PopAllEvents();
700 ASSERT_EQ(1u, events
.size());
701 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndPosition"));
703 client
->OnMouseReleased();
705 client
->OnStatus(toplevel
, false, None
);
707 events
= collector
.PopAllEvents();
708 ASSERT_EQ(1u, events
.size());
709 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndLeave"));
710 EXPECT_FALSE(client
->IsMoveLoopRunning());
713 void RejectAfterMouseReleaseStep3(TestDragDropClient
* client
) {
714 EXPECT_TRUE(client
->IsMoveLoopRunning());
717 ClientMessageEventCollector
collector(toplevel
, client
);
718 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
720 std::vector
<XClientMessageEvent
> events
= collector
.PopAllEvents();
721 ASSERT_EQ(2u, events
.size());
722 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndEnter"));
723 EXPECT_TRUE(client
->MessageHasType(events
[1], "XdndPosition"));
725 client
->OnStatus(toplevel
, true, client
->GetAtom("XdndActionCopy"));
726 EXPECT_FALSE(collector
.HasEvents());
728 client
->OnMouseReleased();
729 events
= collector
.PopAllEvents();
730 ASSERT_EQ(1u, events
.size());
731 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndDrop"));
733 EXPECT_TRUE(client
->IsMoveLoopRunning());
734 client
->OnFinished(toplevel
, false, None
);
735 EXPECT_FALSE(client
->IsMoveLoopRunning());
740 // Test that the source sends XdndLeave instead of XdndDrop if the drag
741 // operation is rejected after the mouse is released.
742 TEST_F(DesktopDragDropClientAuraX11Test
, RejectAfterMouseRelease
) {
743 base::MessageLoop::current()->PostTask(
745 base::Bind(&RejectAfterMouseReleaseStep2
, client()));
746 int result
= StartDragAndDrop();
747 EXPECT_EQ(ui::DragDropTypes::DRAG_NONE
, result
);
749 // Repeat the test but reject the drop in the XdndFinished message instead.
750 base::MessageLoop::current()->PostTask(
752 base::Bind(&RejectAfterMouseReleaseStep3
, client()));
753 result
= StartDragAndDrop();
754 EXPECT_EQ(ui::DragDropTypes::DRAG_NONE
, result
);
759 // DragDropDelegate which counts the number of each type of drag-drop event and
760 // keeps track of the most recent drag-drop event.
761 class TestDragDropDelegate
: public aura::client::DragDropDelegate
{
763 TestDragDropDelegate()
768 last_event_flags_(0) {}
769 ~TestDragDropDelegate() override
{}
771 int num_enters() const { return num_enters_
; }
772 int num_updates() const { return num_updates_
; }
773 int num_exits() const { return num_exits_
; }
774 int num_drops() const { return num_drops_
; }
775 gfx::Point
last_event_mouse_position() const {
776 return last_event_mouse_position_
;
778 int last_event_flags() const { return last_event_flags_
; }
781 // aura::client::DragDropDelegate:
782 void OnDragEntered(const ui::DropTargetEvent
& event
) override
{
784 last_event_mouse_position_
= event
.location();
785 last_event_flags_
= event
.flags();
788 int OnDragUpdated(const ui::DropTargetEvent
& event
) override
{
790 last_event_mouse_position_
= event
.location();
791 last_event_flags_
= event
.flags();
792 return ui::DragDropTypes::DRAG_COPY
;
795 void OnDragExited() override
{
799 int OnPerformDrop(const ui::DropTargetEvent
& event
) override
{
801 last_event_mouse_position_
= event
.location();
802 last_event_flags_
= event
.flags();
803 return ui::DragDropTypes::DRAG_COPY
;
811 gfx::Point last_event_mouse_position_
;
812 int last_event_flags_
;
814 DISALLOW_COPY_AND_ASSIGN(TestDragDropDelegate
);
819 // Test harness for tests where the drag and drop source and target are both
821 class DesktopDragDropClientAuraX11ChromeSourceTargetTest
822 : public ViewsTestBase
{
824 DesktopDragDropClientAuraX11ChromeSourceTargetTest() {
827 ~DesktopDragDropClientAuraX11ChromeSourceTargetTest() override
{}
829 int StartDragAndDrop() {
830 ui::OSExchangeData data
;
831 data
.SetString(base::ASCIIToUTF16("Test"));
833 return client_
->StartDragAndDrop(
835 widget_
->GetNativeWindow()->GetRootWindow(),
836 widget_
->GetNativeWindow(),
838 ui::DragDropTypes::DRAG_COPY
,
839 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE
);
843 void SetUp() override
{
844 ViewsTestBase::SetUp();
846 // Create widget to initiate the drags.
847 widget_
.reset(new Widget
);
848 Widget::InitParams
params(Widget::InitParams::TYPE_WINDOW
);
849 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
850 params
.native_widget
= new DesktopNativeWidgetAura(widget_
.get());
851 params
.bounds
= gfx::Rect(100, 100);
852 widget_
->Init(params
);
855 cursor_manager_
.reset(new DesktopNativeCursorManager(
856 DesktopCursorLoaderUpdater::Create()));
858 client_
.reset(new SimpleTestDragDropClient(widget_
->GetNativeWindow(),
859 cursor_manager_
.get()));
863 void TearDown() override
{
865 cursor_manager_
.reset();
867 ViewsTestBase::TearDown();
870 SimpleTestDragDropClient
* client() {
871 return client_
.get();
875 scoped_ptr
<SimpleTestDragDropClient
> client_
;
876 scoped_ptr
<DesktopNativeCursorManager
> cursor_manager_
;
878 // The widget used to initiate drags.
879 scoped_ptr
<Widget
> widget_
;
881 DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11ChromeSourceTargetTest
);
886 void ChromeSourceTargetStep2(SimpleTestDragDropClient
* client
,
887 int modifier_flags
) {
888 EXPECT_TRUE(client
->IsMoveLoopRunning());
890 scoped_ptr
<Widget
> target_widget(new Widget
);
891 Widget::InitParams
target_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
892 target_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
893 target_params
.native_widget
=
894 new DesktopNativeWidgetAura(target_widget
.get());
895 target_params
.bounds
= gfx::Rect(100, 100);
896 target_widget
->Init(target_params
);
897 target_widget
->Show();
899 TestDragDropDelegate
* delegate
= new TestDragDropDelegate
;
900 aura::client::SetDragDropDelegate(target_widget
->GetNativeWindow(), delegate
);
902 client
->SetTopmostXWindow(
903 target_widget
->GetNativeView()->GetHost()->GetAcceleratedWidget());
905 gfx::Rect target_widget_bounds_in_screen
=
906 target_widget
->GetWindowBoundsInScreen();
907 gfx::Point point1_in_screen
= target_widget_bounds_in_screen
.CenterPoint();
908 gfx::Point
point1_in_target_widget(
909 target_widget_bounds_in_screen
.width() / 2,
910 target_widget_bounds_in_screen
.height() / 2);
911 gfx::Point point2_in_screen
= point1_in_screen
+ gfx::Vector2d(1, 0);
912 gfx::Point point2_in_target_widget
=
913 point1_in_target_widget
+ gfx::Vector2d(1, 0);
915 client
->OnMouseMovement(point1_in_screen
, modifier_flags
,
916 ui::EventTimeForNow());
917 EXPECT_EQ(1, delegate
->num_enters());
918 EXPECT_EQ(1, delegate
->num_updates());
919 EXPECT_EQ(0, delegate
->num_exits());
920 EXPECT_EQ(0, delegate
->num_drops());
921 EXPECT_EQ(point1_in_target_widget
.ToString(),
922 delegate
->last_event_mouse_position().ToString());
923 EXPECT_EQ(modifier_flags
, delegate
->last_event_flags());
925 client
->OnMouseMovement(point2_in_screen
, modifier_flags
,
926 ui::EventTimeForNow());
927 EXPECT_EQ(1, delegate
->num_enters());
928 EXPECT_EQ(2, delegate
->num_updates());
929 EXPECT_EQ(0, delegate
->num_exits());
930 EXPECT_EQ(0, delegate
->num_drops());
931 EXPECT_EQ(point2_in_target_widget
.ToString(),
932 delegate
->last_event_mouse_position().ToString());
933 EXPECT_EQ(modifier_flags
, delegate
->last_event_flags());
935 client
->OnMouseReleased();
936 EXPECT_EQ(1, delegate
->num_enters());
937 EXPECT_EQ(2, delegate
->num_updates());
938 EXPECT_EQ(0, delegate
->num_exits());
939 EXPECT_EQ(1, delegate
->num_drops());
940 EXPECT_EQ(point2_in_target_widget
.ToString(),
941 delegate
->last_event_mouse_position().ToString());
942 EXPECT_EQ(modifier_flags
, delegate
->last_event_flags());
944 EXPECT_FALSE(client
->IsMoveLoopRunning());
949 TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest
, Basic
) {
950 base::MessageLoop::current()->PostTask(
952 base::Bind(&ChromeSourceTargetStep2
, client(), ui::EF_NONE
));
953 int result
= StartDragAndDrop();
954 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY
, result
);
957 // Test that if 'Ctrl' is pressed during a drag and drop operation, that
958 // the aura::client::DragDropDelegate is properly notified.
959 TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest
, CtrlPressed
) {
960 base::MessageLoop::current()->PostTask(
962 base::Bind(&ChromeSourceTargetStep2
, client(), ui::EF_CONTROL_DOWN
));
963 int result
= StartDragAndDrop();
964 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY
, result
);