Update V8 to version 4.7.57.
[chromium-blink-merge.git] / ui / views / widget / desktop_aura / desktop_drag_drop_client_aurax11_unittest.cc
blob61dd76d758015528871ec121f98881b3c090ba55
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 // Map of ::Windows to the collector which intercepts XClientMessageEvents
188 // for that window.
189 std::map< ::Window, ClientMessageEventCollector*> collectors_;
191 ui::X11AtomCache atom_cache_;
193 DISALLOW_COPY_AND_ASSIGN(TestDragDropClient);
196 ///////////////////////////////////////////////////////////////////////////////
197 // ClientMessageEventCollector
199 ClientMessageEventCollector::ClientMessageEventCollector(
200 ::Window xid,
201 TestDragDropClient* client)
202 : xid_(xid),
203 client_(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_);
214 return to_return;
217 void ClientMessageEventCollector::RecordEvent(
218 const XClientMessageEvent& event) {
219 events_.push_back(event);
222 ///////////////////////////////////////////////////////////////////////////////
223 // TestMoveLoop
225 TestMoveLoop::TestMoveLoop(X11MoveLoopDelegate* delegate)
226 : delegate_(delegate),
227 is_running_(false) {
230 TestMoveLoop::~TestMoveLoop() {
233 bool TestMoveLoop::IsRunning() const {
234 return is_running_;
237 bool TestMoveLoop::RunMoveLoop(
238 aura::Window* window,
239 gfx::NativeCursor cursor) {
240 is_running_ = true;
241 base::RunLoop run_loop;
242 quit_closure_ = run_loop.QuitClosure();
243 run_loop.Run();
244 return true;
247 void TestMoveLoop::UpdateCursor(gfx::NativeCursor cursor) {
250 void TestMoveLoop::EndMoveLoop() {
251 if (is_running_) {
252 delegate_->OnMoveLoopEnded();
253 is_running_ = false;
254 quit_closure_.Run();
258 ///////////////////////////////////////////////////////////////////////////////
259 // SimpleTestDragDropClient
261 SimpleTestDragDropClient::SimpleTestDragDropClient(
262 aura::Window* window,
263 DesktopNativeCursorManager* cursor_manager)
264 : DesktopDragDropClientAuraX11(window,
265 cursor_manager,
266 gfx::GetXDisplay(),
267 window->GetHost()->GetAcceleratedWidget()),
268 target_xid_(None),
269 loop_(NULL) {
272 SimpleTestDragDropClient::~SimpleTestDragDropClient() {
275 void SimpleTestDragDropClient::SetTopmostXWindow(XID xid) {
276 target_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) {
290 return target_xid_;
293 ///////////////////////////////////////////////////////////////////////////////
294 // TestDragDropClient
296 // static
297 const int TestDragDropClient::kMouseMoveX = 100;
299 // static
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,
318 const char* type) {
319 return event.message_type == atom_cache_.GetAtom(type);
322 void TestDragDropClient::SetEventCollectorFor(
323 ::Window xid,
324 ClientMessageEventCollector* collector) {
325 if (collector)
326 collectors_[xid] = collector;
327 else
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");
336 event.format = 32;
337 event.window = source_xid_;
338 event.data.l[0] = target_window;
339 event.data.l[1] = will_accept_drop ? 1 : 0;
340 event.data.l[2] = 0;
341 event.data.l[3] = 0;
342 event.data.l[4] = accepted_action;
343 OnXdndStatus(event);
346 void TestDragDropClient::OnFinished(XID target_window,
347 bool accepted_drop,
348 ::Atom performed_action) {
349 XClientMessageEvent event;
350 event.message_type = atom_cache_.GetAtom("XdndFinished");
351 event.format = 32;
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;
356 event.data.l[3] = 0;
357 event.data.l[4] = 0;
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);
374 } // namespace
376 class DesktopDragDropClientAuraX11Test : public ViewsTestBase {
377 public:
378 DesktopDragDropClientAuraX11Test() {
381 ~DesktopDragDropClientAuraX11Test() override {}
383 int StartDragAndDrop() {
384 ui::OSExchangeData data;
385 data.SetString(base::ASCIIToUTF16("Test"));
387 return client_->StartDragAndDrop(
388 data,
389 widget_->GetNativeWindow()->GetRootWindow(),
390 widget_->GetNativeWindow(),
391 gfx::Point(),
392 ui::DragDropTypes::DRAG_COPY,
393 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
396 // ViewsTestBase:
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);
407 widget_->Show();
409 cursor_manager_.reset(new DesktopNativeCursorManager(
410 DesktopCursorLoaderUpdater::Create()));
412 client_.reset(new TestDragDropClient(widget_->GetNativeWindow(),
413 cursor_manager_.get()));
414 client_->Init();
417 void TearDown() override {
418 client_.reset();
419 cursor_manager_.reset();
420 widget_.reset();
421 ViewsTestBase::TearDown();
424 TestDragDropClient* client() {
425 return client_.get();
428 private:
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);
438 namespace {
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
447 // message.
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]));
462 const long kCoords =
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());
520 } // namespace
522 TEST_F(DesktopDragDropClientAuraX11Test, Basic) {
523 XID toplevel = 1;
525 base::MessageLoop::current()->PostTask(FROM_HERE,
526 base::Bind(&BasicStep2,
527 client(),
528 toplevel));
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,
536 client(),
537 toplevel));
538 result = StartDragAndDrop();
539 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
542 namespace {
544 void TargetDoesNotRespondStep2(TestDragDropClient* client) {
545 EXPECT_TRUE(client->IsMoveLoopRunning());
547 XID toplevel = 1;
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());
563 } // namespace
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(
571 FROM_HERE,
572 base::Bind(&TargetDoesNotRespondStep2, client()));
573 int result = StartDragAndDrop();
574 EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result);
577 namespace {
579 void QueuePositionStep2(TestDragDropClient* client) {
580 EXPECT_TRUE(client->IsMoveLoopRunning());
582 XID toplevel = 1;
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());
611 } // namespace
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(
617 FROM_HERE,
618 base::Bind(&QueuePositionStep2, client()));
619 int result = StartDragAndDrop();
620 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
623 namespace {
625 void TargetChangesStep2(TestDragDropClient* client) {
626 EXPECT_TRUE(client->IsMoveLoopRunning());
628 XID toplevel1 = 1;
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"));
637 XID toplevel2 = 2;
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());
665 } // namespace
667 // Test the behavior when the target changes during a drag.
668 TEST_F(DesktopDragDropClientAuraX11Test, TargetChanges) {
669 base::MessageLoop::current()->PostTask(
670 FROM_HERE,
671 base::Bind(&TargetChangesStep2, client()));
672 int result = StartDragAndDrop();
673 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
676 namespace {
678 void RejectAfterMouseReleaseStep2(TestDragDropClient* client) {
679 EXPECT_TRUE(client->IsMoveLoopRunning());
681 XID toplevel = 1;
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();
700 // Reject the drop.
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());
712 XID toplevel = 2;
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());
734 } // namespace
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(
740 FROM_HERE,
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(
747 FROM_HERE,
748 base::Bind(&RejectAfterMouseReleaseStep3, client()));
749 result = StartDragAndDrop();
750 EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result);
753 namespace {
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 {
758 public:
759 TestDragDropDelegate()
760 : num_enters_(0),
761 num_updates_(0),
762 num_exits_(0),
763 num_drops_(0),
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_; }
776 private:
777 // aura::client::DragDropDelegate:
778 void OnDragEntered(const ui::DropTargetEvent& event) override {
779 ++num_enters_;
780 last_event_mouse_position_ = event.location();
781 last_event_flags_ = event.flags();
784 int OnDragUpdated(const ui::DropTargetEvent& event) override {
785 ++num_updates_;
786 last_event_mouse_position_ = event.location();
787 last_event_flags_ = event.flags();
788 return ui::DragDropTypes::DRAG_COPY;
791 void OnDragExited() override {
792 ++num_exits_;
795 int OnPerformDrop(const ui::DropTargetEvent& event) override {
796 ++num_drops_;
797 last_event_mouse_position_ = event.location();
798 last_event_flags_ = event.flags();
799 return ui::DragDropTypes::DRAG_COPY;
802 int num_enters_;
803 int num_updates_;
804 int num_exits_;
805 int num_drops_;
807 gfx::Point last_event_mouse_position_;
808 int last_event_flags_;
810 DISALLOW_COPY_AND_ASSIGN(TestDragDropDelegate);
813 } // namespace
815 // Test harness for tests where the drag and drop source and target are both
816 // Chrome windows.
817 class DesktopDragDropClientAuraX11ChromeSourceTargetTest
818 : public ViewsTestBase {
819 public:
820 DesktopDragDropClientAuraX11ChromeSourceTargetTest() {
823 ~DesktopDragDropClientAuraX11ChromeSourceTargetTest() override {}
825 int StartDragAndDrop() {
826 ui::OSExchangeData data;
827 data.SetString(base::ASCIIToUTF16("Test"));
829 return client_->StartDragAndDrop(
830 data,
831 widget_->GetNativeWindow()->GetRootWindow(),
832 widget_->GetNativeWindow(),
833 gfx::Point(),
834 ui::DragDropTypes::DRAG_COPY,
835 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
838 // ViewsTestBase:
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);
849 widget_->Show();
851 cursor_manager_.reset(new DesktopNativeCursorManager(
852 DesktopCursorLoaderUpdater::Create()));
854 client_.reset(new SimpleTestDragDropClient(widget_->GetNativeWindow(),
855 cursor_manager_.get()));
856 client_->Init();
859 void TearDown() override {
860 client_.reset();
861 cursor_manager_.reset();
862 widget_.reset();
863 ViewsTestBase::TearDown();
866 SimpleTestDragDropClient* client() {
867 return client_.get();
870 private:
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);
880 namespace {
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());
943 } // namespace
945 TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest, Basic) {
946 base::MessageLoop::current()->PostTask(
947 FROM_HERE,
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(
957 FROM_HERE,
958 base::Bind(&ChromeSourceTargetStep2, client(), ui::EF_CONTROL_DOWN));
959 int result = StartDragAndDrop();
960 EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
963 } // namespace views