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 // Map of ::Windows to the collector which intercepts XClientMessageEvents
189 std::map
< ::Window
, ClientMessageEventCollector
*> collectors_
;
191 ui::X11AtomCache atom_cache_
;
193 DISALLOW_COPY_AND_ASSIGN(TestDragDropClient
);
196 ///////////////////////////////////////////////////////////////////////////////
197 // ClientMessageEventCollector
199 ClientMessageEventCollector::ClientMessageEventCollector(
201 TestDragDropClient
* client
)
204 client
->SetEventCollectorFor(xid
, this);
207 ClientMessageEventCollector::~ClientMessageEventCollector() {
208 client_
->SetEventCollectorFor(xid_
, NULL
);
211 std::vector
<XClientMessageEvent
> ClientMessageEventCollector::PopAllEvents() {
212 std::vector
<XClientMessageEvent
> to_return
;
213 to_return
.swap(events_
);
217 void ClientMessageEventCollector::RecordEvent(
218 const XClientMessageEvent
& event
) {
219 events_
.push_back(event
);
222 ///////////////////////////////////////////////////////////////////////////////
225 TestMoveLoop::TestMoveLoop(X11MoveLoopDelegate
* delegate
)
226 : delegate_(delegate
),
230 TestMoveLoop::~TestMoveLoop() {
233 bool TestMoveLoop::IsRunning() const {
237 bool TestMoveLoop::RunMoveLoop(
238 aura::Window
* window
,
239 gfx::NativeCursor cursor
) {
241 base::RunLoop run_loop
;
242 quit_closure_
= run_loop
.QuitClosure();
247 void TestMoveLoop::UpdateCursor(gfx::NativeCursor cursor
) {
250 void TestMoveLoop::EndMoveLoop() {
252 delegate_
->OnMoveLoopEnded();
258 ///////////////////////////////////////////////////////////////////////////////
259 // SimpleTestDragDropClient
261 SimpleTestDragDropClient::SimpleTestDragDropClient(
262 aura::Window
* window
,
263 DesktopNativeCursorManager
* cursor_manager
)
264 : DesktopDragDropClientAuraX11(window
,
267 window
->GetHost()->GetAcceleratedWidget()),
272 SimpleTestDragDropClient::~SimpleTestDragDropClient() {
275 void SimpleTestDragDropClient::SetTopmostXWindow(XID xid
) {
279 bool SimpleTestDragDropClient::IsMoveLoopRunning() {
280 return loop_
->IsRunning();
283 scoped_ptr
<X11MoveLoop
> SimpleTestDragDropClient::CreateMoveLoop(
284 X11MoveLoopDelegate
* delegate
) {
285 loop_
= new TestMoveLoop(delegate
);
286 return make_scoped_ptr(loop_
);
289 XID
SimpleTestDragDropClient::FindWindowFor(const gfx::Point
& screen_point
) {
293 ///////////////////////////////////////////////////////////////////////////////
294 // TestDragDropClient
297 const int TestDragDropClient::kMouseMoveX
= 100;
300 const int TestDragDropClient::kMouseMoveY
= 200;
302 TestDragDropClient::TestDragDropClient(
303 aura::Window
* window
,
304 DesktopNativeCursorManager
* cursor_manager
)
305 : SimpleTestDragDropClient(window
, cursor_manager
),
306 source_xid_(window
->GetHost()->GetAcceleratedWidget()),
307 atom_cache_(gfx::GetXDisplay(), kAtomsToCache
) {
310 TestDragDropClient::~TestDragDropClient() {
313 Atom
TestDragDropClient::GetAtom(const char* name
) {
314 return atom_cache_
.GetAtom(name
);
317 bool TestDragDropClient::MessageHasType(const XClientMessageEvent
& event
,
319 return event
.message_type
== atom_cache_
.GetAtom(type
);
322 void TestDragDropClient::SetEventCollectorFor(
324 ClientMessageEventCollector
* collector
) {
326 collectors_
[xid
] = collector
;
328 collectors_
.erase(xid
);
331 void TestDragDropClient::OnStatus(XID target_window
,
332 bool will_accept_drop
,
333 ::Atom accepted_action
) {
334 XClientMessageEvent event
;
335 event
.message_type
= atom_cache_
.GetAtom("XdndStatus");
337 event
.window
= source_xid_
;
338 event
.data
.l
[0] = target_window
;
339 event
.data
.l
[1] = will_accept_drop
? 1 : 0;
342 event
.data
.l
[4] = accepted_action
;
346 void TestDragDropClient::OnFinished(XID target_window
,
348 ::Atom performed_action
) {
349 XClientMessageEvent event
;
350 event
.message_type
= atom_cache_
.GetAtom("XdndFinished");
352 event
.window
= source_xid_
;
353 event
.data
.l
[0] = target_window
;
354 event
.data
.l
[1] = accepted_drop
? 1 : 0;
355 event
.data
.l
[2] = performed_action
;
358 OnXdndFinished(event
);
361 void TestDragDropClient::SetTopmostXWindowAndMoveMouse(::Window xid
) {
362 SetTopmostXWindow(xid
);
363 OnMouseMovement(gfx::Point(kMouseMoveX
, kMouseMoveY
), ui::EF_NONE
,
364 ui::EventTimeForNow());
367 void TestDragDropClient::SendXClientEvent(::Window xid
, XEvent
* event
) {
368 std::map
< ::Window
, ClientMessageEventCollector
*>::iterator it
=
369 collectors_
.find(xid
);
370 if (it
!= collectors_
.end())
371 it
->second
->RecordEvent(event
->xclient
);
376 class DesktopDragDropClientAuraX11Test
: public ViewsTestBase
{
378 DesktopDragDropClientAuraX11Test() {
381 ~DesktopDragDropClientAuraX11Test() override
{}
383 int StartDragAndDrop() {
384 ui::OSExchangeData data
;
385 data
.SetString(base::ASCIIToUTF16("Test"));
387 return client_
->StartDragAndDrop(
389 widget_
->GetNativeWindow()->GetRootWindow(),
390 widget_
->GetNativeWindow(),
392 ui::DragDropTypes::DRAG_COPY
,
393 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE
);
397 void SetUp() override
{
398 ViewsTestBase::SetUp();
400 // Create widget to initiate the drags.
401 widget_
.reset(new Widget
);
402 Widget::InitParams
params(Widget::InitParams::TYPE_WINDOW
);
403 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
404 params
.native_widget
= new DesktopNativeWidgetAura(widget_
.get());
405 params
.bounds
= gfx::Rect(100, 100);
406 widget_
->Init(params
);
409 cursor_manager_
.reset(new DesktopNativeCursorManager(
410 DesktopCursorLoaderUpdater::Create()));
412 client_
.reset(new TestDragDropClient(widget_
->GetNativeWindow(),
413 cursor_manager_
.get()));
417 void TearDown() override
{
419 cursor_manager_
.reset();
421 ViewsTestBase::TearDown();
424 TestDragDropClient
* client() {
425 return client_
.get();
429 scoped_ptr
<TestDragDropClient
> client_
;
430 scoped_ptr
<DesktopNativeCursorManager
> cursor_manager_
;
432 // The widget used to initiate drags.
433 scoped_ptr
<Widget
> widget_
;
435 DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11Test
);
440 void BasicStep2(TestDragDropClient
* client
, XID toplevel
) {
441 EXPECT_TRUE(client
->IsMoveLoopRunning());
443 ClientMessageEventCollector
collector(toplevel
, client
);
444 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
446 // XdndEnter should have been sent to |toplevel| before the XdndPosition
448 std::vector
<XClientMessageEvent
> events
= collector
.PopAllEvents();
449 ASSERT_EQ(2u, events
.size());
451 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndEnter"));
452 EXPECT_EQ(client
->source_xwindow(),
453 static_cast<XID
>(events
[0].data
.l
[0]));
454 EXPECT_EQ(1, events
[0].data
.l
[1] & 1);
455 std::vector
<Atom
> targets
;
456 ui::GetAtomArrayProperty(client
->source_xwindow(), "XdndTypeList", &targets
);
457 EXPECT_FALSE(targets
.empty());
459 EXPECT_TRUE(client
->MessageHasType(events
[1], "XdndPosition"));
460 EXPECT_EQ(client
->source_xwindow(),
461 static_cast<XID
>(events
[0].data
.l
[0]));
463 TestDragDropClient::kMouseMoveX
<< 16 | TestDragDropClient::kMouseMoveY
;
464 EXPECT_EQ(kCoords
, events
[1].data
.l
[2]);
465 EXPECT_EQ(client
->GetAtom("XdndActionCopy"),
466 static_cast<Atom
>(events
[1].data
.l
[4]));
468 client
->OnStatus(toplevel
, true, client
->GetAtom("XdndActionCopy"));
470 // Because there is no unprocessed XdndPosition, the drag drop client should
471 // send XdndDrop immediately after the mouse is released.
472 client
->OnMouseReleased();
474 events
= collector
.PopAllEvents();
475 ASSERT_EQ(1u, events
.size());
476 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndDrop"));
477 EXPECT_EQ(client
->source_xwindow(),
478 static_cast<XID
>(events
[0].data
.l
[0]));
480 // Send XdndFinished to indicate that the drag drop client can cleanup any
481 // data related to this drag. The move loop should end only after the
482 // XdndFinished message was received.
483 EXPECT_TRUE(client
->IsMoveLoopRunning());
484 client
->OnFinished(toplevel
, true, client
->GetAtom("XdndActionCopy"));
485 EXPECT_FALSE(client
->IsMoveLoopRunning());
488 void BasicStep3(TestDragDropClient
* client
, XID toplevel
) {
489 EXPECT_TRUE(client
->IsMoveLoopRunning());
491 ClientMessageEventCollector
collector(toplevel
, client
);
492 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
494 std::vector
<XClientMessageEvent
> events
= collector
.PopAllEvents();
495 ASSERT_EQ(2u, events
.size());
496 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndEnter"));
497 EXPECT_TRUE(client
->MessageHasType(events
[1], "XdndPosition"));
499 client
->OnStatus(toplevel
, true, client
->GetAtom("XdndActionCopy"));
500 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
501 events
= collector
.PopAllEvents();
502 ASSERT_EQ(1u, events
.size());
503 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndPosition"));
505 // We have not received an XdndStatus ack for the second XdndPosition message.
506 // Test that sending XdndDrop is delayed till the XdndStatus ack is received.
507 client
->OnMouseReleased();
508 EXPECT_FALSE(collector
.HasEvents());
510 client
->OnStatus(toplevel
, true, client
->GetAtom("XdndActionCopy"));
511 events
= collector
.PopAllEvents();
512 ASSERT_EQ(1u, events
.size());
513 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndDrop"));
515 EXPECT_TRUE(client
->IsMoveLoopRunning());
516 client
->OnFinished(toplevel
, true, client
->GetAtom("XdndActionCopy"));
517 EXPECT_FALSE(client
->IsMoveLoopRunning());
522 TEST_F(DesktopDragDropClientAuraX11Test
, Basic
) {
525 base::MessageLoop::current()->PostTask(FROM_HERE
,
526 base::Bind(&BasicStep2
,
529 int result
= StartDragAndDrop();
530 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY
, result
);
532 // Do another drag and drop to test that the data is properly cleaned up as a
533 // result of the XdndFinished message.
534 base::MessageLoop::current()->PostTask(FROM_HERE
,
535 base::Bind(&BasicStep3
,
538 result
= StartDragAndDrop();
539 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY
, result
);
544 void TargetDoesNotRespondStep2(TestDragDropClient
* client
) {
545 EXPECT_TRUE(client
->IsMoveLoopRunning());
548 ClientMessageEventCollector
collector(toplevel
, client
);
549 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
551 std::vector
<XClientMessageEvent
> events
= collector
.PopAllEvents();
552 ASSERT_EQ(2u, events
.size());
553 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndEnter"));
554 EXPECT_TRUE(client
->MessageHasType(events
[1], "XdndPosition"));
556 client
->OnMouseReleased();
557 events
= collector
.PopAllEvents();
558 ASSERT_EQ(1u, events
.size());
559 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndLeave"));
560 EXPECT_FALSE(client
->IsMoveLoopRunning());
565 // Test that we do not wait for the target to send XdndStatus if we have not
566 // received any XdndStatus messages at all from the target. The Unity
567 // DNDCollectionWindow is an example of an XdndAware target which does not
568 // respond to XdndPosition messages at all.
569 TEST_F(DesktopDragDropClientAuraX11Test
, TargetDoesNotRespond
) {
570 base::MessageLoop::current()->PostTask(
572 base::Bind(&TargetDoesNotRespondStep2
, client()));
573 int result
= StartDragAndDrop();
574 EXPECT_EQ(ui::DragDropTypes::DRAG_NONE
, result
);
579 void QueuePositionStep2(TestDragDropClient
* client
) {
580 EXPECT_TRUE(client
->IsMoveLoopRunning());
583 ClientMessageEventCollector
collector(toplevel
, client
);
584 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
585 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
586 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
588 std::vector
<XClientMessageEvent
> events
= collector
.PopAllEvents();
589 ASSERT_EQ(2u, events
.size());
590 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndEnter"));
591 EXPECT_TRUE(client
->MessageHasType(events
[1], "XdndPosition"));
593 client
->OnStatus(toplevel
, true, client
->GetAtom("XdndActionCopy"));
594 events
= collector
.PopAllEvents();
595 ASSERT_EQ(1u, events
.size());
596 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndPosition"));
598 client
->OnStatus(toplevel
, true, client
->GetAtom("XdndActionCopy"));
599 EXPECT_FALSE(collector
.HasEvents());
601 client
->OnMouseReleased();
602 events
= collector
.PopAllEvents();
603 ASSERT_EQ(1u, events
.size());
604 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndDrop"));
606 EXPECT_TRUE(client
->IsMoveLoopRunning());
607 client
->OnFinished(toplevel
, true, client
->GetAtom("XdndActionCopy"));
608 EXPECT_FALSE(client
->IsMoveLoopRunning());
613 // Test that XdndPosition messages are queued till the pending XdndPosition
614 // message is acked via an XdndStatus message.
615 TEST_F(DesktopDragDropClientAuraX11Test
, QueuePosition
) {
616 base::MessageLoop::current()->PostTask(
618 base::Bind(&QueuePositionStep2
, client()));
619 int result
= StartDragAndDrop();
620 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY
, result
);
625 void TargetChangesStep2(TestDragDropClient
* client
) {
626 EXPECT_TRUE(client
->IsMoveLoopRunning());
629 ClientMessageEventCollector
collector1(toplevel1
, client
);
630 client
->SetTopmostXWindowAndMoveMouse(toplevel1
);
632 std::vector
<XClientMessageEvent
> events1
= collector1
.PopAllEvents();
633 ASSERT_EQ(2u, events1
.size());
634 EXPECT_TRUE(client
->MessageHasType(events1
[0], "XdndEnter"));
635 EXPECT_TRUE(client
->MessageHasType(events1
[1], "XdndPosition"));
638 ClientMessageEventCollector
collector2(toplevel2
, client
);
639 client
->SetTopmostXWindowAndMoveMouse(toplevel2
);
641 // It is possible for |toplevel1| to send XdndStatus after the source has sent
642 // XdndLeave but before |toplevel1| has received the XdndLeave message. The
643 // XdndStatus message should be ignored.
644 client
->OnStatus(toplevel1
, true, client
->GetAtom("XdndActionCopy"));
645 events1
= collector1
.PopAllEvents();
646 ASSERT_EQ(1u, events1
.size());
647 EXPECT_TRUE(client
->MessageHasType(events1
[0], "XdndLeave"));
649 std::vector
<XClientMessageEvent
> events2
= collector2
.PopAllEvents();
650 ASSERT_EQ(2u, events2
.size());
651 EXPECT_TRUE(client
->MessageHasType(events2
[0], "XdndEnter"));
652 EXPECT_TRUE(client
->MessageHasType(events2
[1], "XdndPosition"));
654 client
->OnStatus(toplevel2
, true, client
->GetAtom("XdndActionCopy"));
655 client
->OnMouseReleased();
656 events2
= collector2
.PopAllEvents();
657 ASSERT_EQ(1u, events2
.size());
658 EXPECT_TRUE(client
->MessageHasType(events2
[0], "XdndDrop"));
660 EXPECT_TRUE(client
->IsMoveLoopRunning());
661 client
->OnFinished(toplevel2
, true, client
->GetAtom("XdndActionCopy"));
662 EXPECT_FALSE(client
->IsMoveLoopRunning());
667 // Test the behavior when the target changes during a drag.
668 TEST_F(DesktopDragDropClientAuraX11Test
, TargetChanges
) {
669 base::MessageLoop::current()->PostTask(
671 base::Bind(&TargetChangesStep2
, client()));
672 int result
= StartDragAndDrop();
673 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY
, result
);
678 void RejectAfterMouseReleaseStep2(TestDragDropClient
* client
) {
679 EXPECT_TRUE(client
->IsMoveLoopRunning());
682 ClientMessageEventCollector
collector(toplevel
, client
);
683 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
685 std::vector
<XClientMessageEvent
> events
= collector
.PopAllEvents();
686 ASSERT_EQ(2u, events
.size());
687 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndEnter"));
688 EXPECT_TRUE(client
->MessageHasType(events
[1], "XdndPosition"));
690 client
->OnStatus(toplevel
, true, client
->GetAtom("XdndActionCopy"));
691 EXPECT_FALSE(collector
.HasEvents());
693 // Send another mouse move such that there is a pending XdndPosition.
694 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
695 events
= collector
.PopAllEvents();
696 ASSERT_EQ(1u, events
.size());
697 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndPosition"));
699 client
->OnMouseReleased();
701 client
->OnStatus(toplevel
, false, None
);
703 events
= collector
.PopAllEvents();
704 ASSERT_EQ(1u, events
.size());
705 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndLeave"));
706 EXPECT_FALSE(client
->IsMoveLoopRunning());
709 void RejectAfterMouseReleaseStep3(TestDragDropClient
* client
) {
710 EXPECT_TRUE(client
->IsMoveLoopRunning());
713 ClientMessageEventCollector
collector(toplevel
, client
);
714 client
->SetTopmostXWindowAndMoveMouse(toplevel
);
716 std::vector
<XClientMessageEvent
> events
= collector
.PopAllEvents();
717 ASSERT_EQ(2u, events
.size());
718 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndEnter"));
719 EXPECT_TRUE(client
->MessageHasType(events
[1], "XdndPosition"));
721 client
->OnStatus(toplevel
, true, client
->GetAtom("XdndActionCopy"));
722 EXPECT_FALSE(collector
.HasEvents());
724 client
->OnMouseReleased();
725 events
= collector
.PopAllEvents();
726 ASSERT_EQ(1u, events
.size());
727 EXPECT_TRUE(client
->MessageHasType(events
[0], "XdndDrop"));
729 EXPECT_TRUE(client
->IsMoveLoopRunning());
730 client
->OnFinished(toplevel
, false, None
);
731 EXPECT_FALSE(client
->IsMoveLoopRunning());
736 // Test that the source sends XdndLeave instead of XdndDrop if the drag
737 // operation is rejected after the mouse is released.
738 TEST_F(DesktopDragDropClientAuraX11Test
, RejectAfterMouseRelease
) {
739 base::MessageLoop::current()->PostTask(
741 base::Bind(&RejectAfterMouseReleaseStep2
, client()));
742 int result
= StartDragAndDrop();
743 EXPECT_EQ(ui::DragDropTypes::DRAG_NONE
, result
);
745 // Repeat the test but reject the drop in the XdndFinished message instead.
746 base::MessageLoop::current()->PostTask(
748 base::Bind(&RejectAfterMouseReleaseStep3
, client()));
749 result
= StartDragAndDrop();
750 EXPECT_EQ(ui::DragDropTypes::DRAG_NONE
, result
);
755 // DragDropDelegate which counts the number of each type of drag-drop event and
756 // keeps track of the most recent drag-drop event.
757 class TestDragDropDelegate
: public aura::client::DragDropDelegate
{
759 TestDragDropDelegate()
764 last_event_flags_(0) {}
765 ~TestDragDropDelegate() override
{}
767 int num_enters() const { return num_enters_
; }
768 int num_updates() const { return num_updates_
; }
769 int num_exits() const { return num_exits_
; }
770 int num_drops() const { return num_drops_
; }
771 gfx::Point
last_event_mouse_position() const {
772 return last_event_mouse_position_
;
774 int last_event_flags() const { return last_event_flags_
; }
777 // aura::client::DragDropDelegate:
778 void OnDragEntered(const ui::DropTargetEvent
& event
) override
{
780 last_event_mouse_position_
= event
.location();
781 last_event_flags_
= event
.flags();
784 int OnDragUpdated(const ui::DropTargetEvent
& event
) override
{
786 last_event_mouse_position_
= event
.location();
787 last_event_flags_
= event
.flags();
788 return ui::DragDropTypes::DRAG_COPY
;
791 void OnDragExited() override
{
795 int OnPerformDrop(const ui::DropTargetEvent
& event
) override
{
797 last_event_mouse_position_
= event
.location();
798 last_event_flags_
= event
.flags();
799 return ui::DragDropTypes::DRAG_COPY
;
807 gfx::Point last_event_mouse_position_
;
808 int last_event_flags_
;
810 DISALLOW_COPY_AND_ASSIGN(TestDragDropDelegate
);
815 // Test harness for tests where the drag and drop source and target are both
817 class DesktopDragDropClientAuraX11ChromeSourceTargetTest
818 : public ViewsTestBase
{
820 DesktopDragDropClientAuraX11ChromeSourceTargetTest() {
823 ~DesktopDragDropClientAuraX11ChromeSourceTargetTest() override
{}
825 int StartDragAndDrop() {
826 ui::OSExchangeData data
;
827 data
.SetString(base::ASCIIToUTF16("Test"));
829 return client_
->StartDragAndDrop(
831 widget_
->GetNativeWindow()->GetRootWindow(),
832 widget_
->GetNativeWindow(),
834 ui::DragDropTypes::DRAG_COPY
,
835 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE
);
839 void SetUp() override
{
840 ViewsTestBase::SetUp();
842 // Create widget to initiate the drags.
843 widget_
.reset(new Widget
);
844 Widget::InitParams
params(Widget::InitParams::TYPE_WINDOW
);
845 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
846 params
.native_widget
= new DesktopNativeWidgetAura(widget_
.get());
847 params
.bounds
= gfx::Rect(100, 100);
848 widget_
->Init(params
);
851 cursor_manager_
.reset(new DesktopNativeCursorManager(
852 DesktopCursorLoaderUpdater::Create()));
854 client_
.reset(new SimpleTestDragDropClient(widget_
->GetNativeWindow(),
855 cursor_manager_
.get()));
859 void TearDown() override
{
861 cursor_manager_
.reset();
863 ViewsTestBase::TearDown();
866 SimpleTestDragDropClient
* client() {
867 return client_
.get();
871 scoped_ptr
<SimpleTestDragDropClient
> client_
;
872 scoped_ptr
<DesktopNativeCursorManager
> cursor_manager_
;
874 // The widget used to initiate drags.
875 scoped_ptr
<Widget
> widget_
;
877 DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11ChromeSourceTargetTest
);
882 void ChromeSourceTargetStep2(SimpleTestDragDropClient
* client
,
883 int modifier_flags
) {
884 EXPECT_TRUE(client
->IsMoveLoopRunning());
886 scoped_ptr
<Widget
> target_widget(new Widget
);
887 Widget::InitParams
target_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
888 target_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
889 target_params
.native_widget
=
890 new DesktopNativeWidgetAura(target_widget
.get());
891 target_params
.bounds
= gfx::Rect(100, 100);
892 target_widget
->Init(target_params
);
893 target_widget
->Show();
895 TestDragDropDelegate
* delegate
= new TestDragDropDelegate
;
896 aura::client::SetDragDropDelegate(target_widget
->GetNativeWindow(), delegate
);
898 client
->SetTopmostXWindow(
899 target_widget
->GetNativeView()->GetHost()->GetAcceleratedWidget());
901 gfx::Rect target_widget_bounds_in_screen
=
902 target_widget
->GetWindowBoundsInScreen();
903 gfx::Point point1_in_screen
= target_widget_bounds_in_screen
.CenterPoint();
904 gfx::Point
point1_in_target_widget(
905 target_widget_bounds_in_screen
.width() / 2,
906 target_widget_bounds_in_screen
.height() / 2);
907 gfx::Point point2_in_screen
= point1_in_screen
+ gfx::Vector2d(1, 0);
908 gfx::Point point2_in_target_widget
=
909 point1_in_target_widget
+ gfx::Vector2d(1, 0);
911 client
->OnMouseMovement(point1_in_screen
, modifier_flags
,
912 ui::EventTimeForNow());
913 EXPECT_EQ(1, delegate
->num_enters());
914 EXPECT_EQ(1, delegate
->num_updates());
915 EXPECT_EQ(0, delegate
->num_exits());
916 EXPECT_EQ(0, delegate
->num_drops());
917 EXPECT_EQ(point1_in_target_widget
.ToString(),
918 delegate
->last_event_mouse_position().ToString());
919 EXPECT_EQ(modifier_flags
, delegate
->last_event_flags());
921 client
->OnMouseMovement(point2_in_screen
, modifier_flags
,
922 ui::EventTimeForNow());
923 EXPECT_EQ(1, delegate
->num_enters());
924 EXPECT_EQ(2, delegate
->num_updates());
925 EXPECT_EQ(0, delegate
->num_exits());
926 EXPECT_EQ(0, delegate
->num_drops());
927 EXPECT_EQ(point2_in_target_widget
.ToString(),
928 delegate
->last_event_mouse_position().ToString());
929 EXPECT_EQ(modifier_flags
, delegate
->last_event_flags());
931 client
->OnMouseReleased();
932 EXPECT_EQ(1, delegate
->num_enters());
933 EXPECT_EQ(2, delegate
->num_updates());
934 EXPECT_EQ(0, delegate
->num_exits());
935 EXPECT_EQ(1, delegate
->num_drops());
936 EXPECT_EQ(point2_in_target_widget
.ToString(),
937 delegate
->last_event_mouse_position().ToString());
938 EXPECT_EQ(modifier_flags
, delegate
->last_event_flags());
940 EXPECT_FALSE(client
->IsMoveLoopRunning());
945 TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest
, Basic
) {
946 base::MessageLoop::current()->PostTask(
948 base::Bind(&ChromeSourceTargetStep2
, client(), ui::EF_NONE
));
949 int result
= StartDragAndDrop();
950 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY
, result
);
953 // Test that if 'Ctrl' is pressed during a drag and drop operation, that
954 // the aura::client::DragDropDelegate is properly notified.
955 TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest
, CtrlPressed
) {
956 base::MessageLoop::current()->PostTask(
958 base::Bind(&ChromeSourceTargetStep2
, client(), ui::EF_CONTROL_DOWN
));
959 int result
= StartDragAndDrop();
960 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY
, result
);