Standardize usage of virtual/override/final specifiers in ui/.
[chromium-blink-merge.git] / ui / views / widget / desktop_aura / desktop_drag_drop_client_aurax11_unittest.cc
blob0eae9dc09b4a2e16fd8ac3cd4e50802d2fa52dc7
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.
5 #include <map>
6 #include <vector>
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"
30 #include <X11/Xlib.h>
32 namespace views {
34 namespace {
36 const char* kAtomsToCache[] = {
37 "XdndActionCopy",
38 "XdndDrop",
39 "XdndEnter",
40 "XdndFinished",
41 "XdndLeave",
42 "XdndPosition",
43 "XdndStatus",
44 "XdndTypeList",
45 NULL
48 class TestDragDropClient;
50 // Collects messages which would otherwise be sent to |xid_| via
51 // SendXClientEvent().
52 class ClientMessageEventCollector {
53 public:
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
63 // were on the stack
64 std::vector<XClientMessageEvent> PopAllEvents();
66 // Adds |event| to the stack.
67 void RecordEvent(const XClientMessageEvent& event);
69 private:
70 ::Window xid_;
72 // Not owned.
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
81 // loop.
82 class TestMoveLoop : public X11MoveLoop {
83 public:
84 explicit TestMoveLoop(X11MoveLoopDelegate* delegate);
85 ~TestMoveLoop() override;
87 // Returns true if the move loop is running.
88 bool IsRunning() const;
90 // X11MoveLoop:
91 bool RunMoveLoop(aura::Window* window, gfx::NativeCursor cursor) override;
92 void UpdateCursor(gfx::NativeCursor cursor) override;
93 void EndMoveLoop() override;
95 private:
96 // Not owned.
97 X11MoveLoopDelegate* delegate_;
99 // Ends the move loop.
100 base::Closure quit_closure_;
102 bool is_running_;
105 // Implementation of DesktopDragDropClientAuraX11 which short circuits
106 // FindWindowFor().
107 class SimpleTestDragDropClient : public DesktopDragDropClientAuraX11 {
108 public:
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();
119 private:
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.
126 XID target_xid_;
128 // The move loop. Not owned.
129 TestMoveLoop* loop_;
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 {
137 public:
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() {
149 return source_xid_;
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,
157 const char* type);
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,
173 bool accepted_drop,
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);
180 private:
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.
188 TestMoveLoop* loop_;
190 // Map of ::Windows to the collector which intercepts XClientMessageEvents
191 // for that window.
192 std::map< ::Window, ClientMessageEventCollector*> collectors_;
194 ui::X11AtomCache atom_cache_;
196 DISALLOW_COPY_AND_ASSIGN(TestDragDropClient);
199 ///////////////////////////////////////////////////////////////////////////////
200 // ClientMessageEventCollector
202 ClientMessageEventCollector::ClientMessageEventCollector(
203 ::Window xid,
204 TestDragDropClient* client)
205 : xid_(xid),
206 client_(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_);
217 return to_return;
220 void ClientMessageEventCollector::RecordEvent(
221 const XClientMessageEvent& event) {
222 events_.push_back(event);
225 ///////////////////////////////////////////////////////////////////////////////
226 // TestMoveLoop
228 TestMoveLoop::TestMoveLoop(X11MoveLoopDelegate* delegate)
229 : delegate_(delegate),
230 is_running_(false) {
233 TestMoveLoop::~TestMoveLoop() {
236 bool TestMoveLoop::IsRunning() const {
237 return is_running_;
240 bool TestMoveLoop::RunMoveLoop(
241 aura::Window* window,
242 gfx::NativeCursor cursor) {
243 is_running_ = true;
244 base::RunLoop run_loop;
245 quit_closure_ = run_loop.QuitClosure();
246 run_loop.Run();
247 return true;
250 void TestMoveLoop::UpdateCursor(gfx::NativeCursor cursor) {
253 void TestMoveLoop::EndMoveLoop() {
254 if (is_running_) {
255 delegate_->OnMoveLoopEnded();
256 is_running_ = false;
257 quit_closure_.Run();
261 ///////////////////////////////////////////////////////////////////////////////
262 // SimpleTestDragDropClient
264 SimpleTestDragDropClient::SimpleTestDragDropClient(
265 aura::Window* window,
266 DesktopNativeCursorManager* cursor_manager)
267 : DesktopDragDropClientAuraX11(window,
268 cursor_manager,
269 gfx::GetXDisplay(),
270 window->GetHost()->GetAcceleratedWidget()),
271 target_xid_(None),
272 loop_(NULL) {
275 SimpleTestDragDropClient::~SimpleTestDragDropClient() {
278 void SimpleTestDragDropClient::SetTopmostXWindow(XID xid) {
279 target_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) {
293 return target_xid_;
296 ///////////////////////////////////////////////////////////////////////////////
297 // TestDragDropClient
299 // static
300 const int TestDragDropClient::kMouseMoveX = 100;
302 // static
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()),
310 loop_(NULL),
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,
322 const char* type) {
323 return event.message_type == atom_cache_.GetAtom(type);
326 void TestDragDropClient::SetEventCollectorFor(
327 ::Window xid,
328 ClientMessageEventCollector* collector) {
329 if (collector)
330 collectors_[xid] = collector;
331 else
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");
340 event.format = 32;
341 event.window = source_xid_;
342 event.data.l[0] = target_window;
343 event.data.l[1] = will_accept_drop ? 1 : 0;
344 event.data.l[2] = 0;
345 event.data.l[3] = 0;
346 event.data.l[4] = accepted_action;
347 OnXdndStatus(event);
350 void TestDragDropClient::OnFinished(XID target_window,
351 bool accepted_drop,
352 ::Atom performed_action) {
353 XClientMessageEvent event;
354 event.message_type = atom_cache_.GetAtom("XdndFinished");
355 event.format = 32;
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;
360 event.data.l[3] = 0;
361 event.data.l[4] = 0;
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);
378 } // namespace
380 class DesktopDragDropClientAuraX11Test : public ViewsTestBase {
381 public:
382 DesktopDragDropClientAuraX11Test() {
385 ~DesktopDragDropClientAuraX11Test() override {}
387 int StartDragAndDrop() {
388 ui::OSExchangeData data;
389 data.SetString(base::ASCIIToUTF16("Test"));
391 return client_->StartDragAndDrop(
392 data,
393 widget_->GetNativeWindow()->GetRootWindow(),
394 widget_->GetNativeWindow(),
395 gfx::Point(),
396 ui::DragDropTypes::DRAG_COPY,
397 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
400 // ViewsTestBase:
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);
411 widget_->Show();
413 cursor_manager_.reset(new DesktopNativeCursorManager(
414 DesktopCursorLoaderUpdater::Create()));
416 client_.reset(new TestDragDropClient(widget_->GetNativeWindow(),
417 cursor_manager_.get()));
418 client_->Init();
421 void TearDown() override {
422 client_.reset();
423 cursor_manager_.reset();
424 widget_.reset();
425 ViewsTestBase::TearDown();
428 TestDragDropClient* client() {
429 return client_.get();
432 private:
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);
442 namespace {
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
451 // message.
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]));
466 const long kCoords =
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());
524 } // namespace
526 TEST_F(DesktopDragDropClientAuraX11Test, Basic) {
527 XID toplevel = 1;
529 base::MessageLoop::current()->PostTask(FROM_HERE,
530 base::Bind(&BasicStep2,
531 client(),
532 toplevel));
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,
540 client(),
541 toplevel));
542 result = StartDragAndDrop();
543 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
546 namespace {
548 void TargetDoesNotRespondStep2(TestDragDropClient* client) {
549 EXPECT_TRUE(client->IsMoveLoopRunning());
551 XID toplevel = 1;
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());
567 } // namespace
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(
575 FROM_HERE,
576 base::Bind(&TargetDoesNotRespondStep2, client()));
577 int result = StartDragAndDrop();
578 EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result);
581 namespace {
583 void QueuePositionStep2(TestDragDropClient* client) {
584 EXPECT_TRUE(client->IsMoveLoopRunning());
586 XID toplevel = 1;
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());
615 } // namespace
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(
621 FROM_HERE,
622 base::Bind(&QueuePositionStep2, client()));
623 int result = StartDragAndDrop();
624 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
627 namespace {
629 void TargetChangesStep2(TestDragDropClient* client) {
630 EXPECT_TRUE(client->IsMoveLoopRunning());
632 XID toplevel1 = 1;
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"));
641 XID toplevel2 = 2;
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());
669 } // namespace
671 // Test the behavior when the target changes during a drag.
672 TEST_F(DesktopDragDropClientAuraX11Test, TargetChanges) {
673 base::MessageLoop::current()->PostTask(
674 FROM_HERE,
675 base::Bind(&TargetChangesStep2, client()));
676 int result = StartDragAndDrop();
677 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
680 namespace {
682 void RejectAfterMouseReleaseStep2(TestDragDropClient* client) {
683 EXPECT_TRUE(client->IsMoveLoopRunning());
685 XID toplevel = 1;
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();
704 // Reject the drop.
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());
716 XID toplevel = 2;
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());
738 } // namespace
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(
744 FROM_HERE,
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(
751 FROM_HERE,
752 base::Bind(&RejectAfterMouseReleaseStep3, client()));
753 result = StartDragAndDrop();
754 EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result);
757 namespace {
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 {
762 public:
763 TestDragDropDelegate()
764 : num_enters_(0),
765 num_updates_(0),
766 num_exits_(0),
767 num_drops_(0),
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_; }
780 private:
781 // aura::client::DragDropDelegate:
782 void OnDragEntered(const ui::DropTargetEvent& event) override {
783 ++num_enters_;
784 last_event_mouse_position_ = event.location();
785 last_event_flags_ = event.flags();
788 int OnDragUpdated(const ui::DropTargetEvent& event) override {
789 ++num_updates_;
790 last_event_mouse_position_ = event.location();
791 last_event_flags_ = event.flags();
792 return ui::DragDropTypes::DRAG_COPY;
795 void OnDragExited() override {
796 ++num_exits_;
799 int OnPerformDrop(const ui::DropTargetEvent& event) override {
800 ++num_drops_;
801 last_event_mouse_position_ = event.location();
802 last_event_flags_ = event.flags();
803 return ui::DragDropTypes::DRAG_COPY;
806 int num_enters_;
807 int num_updates_;
808 int num_exits_;
809 int num_drops_;
811 gfx::Point last_event_mouse_position_;
812 int last_event_flags_;
814 DISALLOW_COPY_AND_ASSIGN(TestDragDropDelegate);
817 } // namespace
819 // Test harness for tests where the drag and drop source and target are both
820 // Chrome windows.
821 class DesktopDragDropClientAuraX11ChromeSourceTargetTest
822 : public ViewsTestBase {
823 public:
824 DesktopDragDropClientAuraX11ChromeSourceTargetTest() {
827 ~DesktopDragDropClientAuraX11ChromeSourceTargetTest() override {}
829 int StartDragAndDrop() {
830 ui::OSExchangeData data;
831 data.SetString(base::ASCIIToUTF16("Test"));
833 return client_->StartDragAndDrop(
834 data,
835 widget_->GetNativeWindow()->GetRootWindow(),
836 widget_->GetNativeWindow(),
837 gfx::Point(),
838 ui::DragDropTypes::DRAG_COPY,
839 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
842 // ViewsTestBase:
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);
853 widget_->Show();
855 cursor_manager_.reset(new DesktopNativeCursorManager(
856 DesktopCursorLoaderUpdater::Create()));
858 client_.reset(new SimpleTestDragDropClient(widget_->GetNativeWindow(),
859 cursor_manager_.get()));
860 client_->Init();
863 void TearDown() override {
864 client_.reset();
865 cursor_manager_.reset();
866 widget_.reset();
867 ViewsTestBase::TearDown();
870 SimpleTestDragDropClient* client() {
871 return client_.get();
874 private:
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);
884 namespace {
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());
947 } // namespace
949 TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest, Basic) {
950 base::MessageLoop::current()->PostTask(
951 FROM_HERE,
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(
961 FROM_HERE,
962 base::Bind(&ChromeSourceTargetStep2, client(), ui::EF_CONTROL_DOWN));
963 int result = StartDragAndDrop();
964 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
967 } // namespace views