Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / ui / views / widget / desktop_aura / desktop_drag_drop_client_aurax11_unittest.cc
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.
4
5 #include <map>
6 #include <vector>
7
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"
11
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/gfx/x/x11_atom_cache.h"
20 #include "ui/gfx/x/x11_types.h"
21 #include "ui/views/widget/desktop_aura/desktop_cursor_loader_updater.h"
22 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h"
23 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
24 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
25 #include "ui/views/widget/desktop_aura/x11_move_loop.h"
26 #include "ui/views/widget/widget.h"
27
28 #include <X11/Xlib.h>
29
30 namespace views {
31
32 namespace {
33
34 const char* kAtomsToCache[] = {
35   "XdndActionCopy",
36   "XdndDrop",
37   "XdndEnter",
38   "XdndFinished",
39   "XdndLeave",
40   "XdndPosition",
41   "XdndStatus",
42   "XdndTypeList",
43   NULL
44 };
45
46 class TestDragDropClient;
47
48 // Collects messages which would otherwise be sent to |xid_| via
49 // SendXClientEvent().
50 class ClientMessageEventCollector {
51  public:
52   ClientMessageEventCollector(::Window xid, TestDragDropClient* client);
53   virtual ~ClientMessageEventCollector();
54
55   // Returns true if |events_| is non-empty.
56   bool HasEvents() const {
57     return !events_.empty();
58   }
59
60   // Pops all of |events_| and returns the popped events in the order that they
61   // were on the stack
62   std::vector<XClientMessageEvent> PopAllEvents();
63
64   // Adds |event| to the stack.
65   void RecordEvent(const XClientMessageEvent& event);
66
67  private:
68   ::Window xid_;
69
70   // Not owned.
71   TestDragDropClient* client_;
72
73   std::vector<XClientMessageEvent> events_;
74
75   DISALLOW_COPY_AND_ASSIGN(ClientMessageEventCollector);
76 };
77
78 // An implementation of X11MoveLoop where RunMoveLoop() always starts the move
79 // loop.
80 class TestMoveLoop : public X11MoveLoop {
81  public:
82   explicit TestMoveLoop(X11MoveLoopDelegate* delegate);
83   virtual ~TestMoveLoop();
84
85   // Returns true if the move loop is running.
86   bool IsRunning() const;
87
88   // X11MoveLoop:
89   virtual bool RunMoveLoop(aura::Window* window,
90                            gfx::NativeCursor cursor) OVERRIDE;
91   virtual void UpdateCursor(gfx::NativeCursor cursor) OVERRIDE;
92   virtual void EndMoveLoop() OVERRIDE;
93
94  private:
95   // Not owned.
96   X11MoveLoopDelegate* delegate_;
97
98   // Ends the move loop.
99   base::Closure quit_closure_;
100
101   bool is_running_;
102 };
103
104 // Implementation of DesktopDragDropClientAuraX11 which works with a fake
105 // |DesktopDragDropClientAuraX11::source_current_window_|.
106 class TestDragDropClient : public DesktopDragDropClientAuraX11 {
107  public:
108   // The location in screen coordinates used for the synthetic mouse moves
109   // generated in SetTopmostXWindowAndMoveMouse().
110   static const int kMouseMoveX;
111   static const int kMouseMoveY;
112
113   TestDragDropClient(aura::Window* window,
114                      DesktopNativeCursorManager* cursor_manager);
115   virtual ~TestDragDropClient();
116
117   // Returns the XID of the window which initiated the drag.
118   ::Window source_xwindow() {
119     return source_xid_;
120   }
121
122   // Returns the Atom with |name|.
123   Atom GetAtom(const char* name);
124
125   // Returns true if the event's message has |type|.
126   bool MessageHasType(const XClientMessageEvent& event,
127                       const char* type);
128
129   // Sets |collector| to collect XClientMessageEvents which would otherwise
130   // have been sent to the drop target window.
131   void SetEventCollectorFor(::Window xid,
132                             ClientMessageEventCollector* collector);
133
134   // Builds an XdndStatus message and sends it to
135   // DesktopDragDropClientAuraX11.
136   void OnStatus(XID target_window,
137                 bool will_accept_drop,
138                 ::Atom accepted_action);
139
140   // Builds an XdndFinished message and sends it to
141   // DesktopDragDropClientAuraX11.
142   void OnFinished(XID target_window,
143                   bool accepted_drop,
144                   ::Atom performed_action);
145
146   // Sets |xid| as the topmost window at the current mouse position and
147   // generates a synthetic mouse move.
148   void SetTopmostXWindowAndMoveMouse(::Window xid);
149
150   // Returns true if the move loop is running.
151   bool IsMoveLoopRunning();
152
153  private:
154   // DesktopDragDropClientAuraX11:
155   virtual scoped_ptr<X11MoveLoop> CreateMoveLoop(
156       X11MoveLoopDelegate* delegate) OVERRIDE;
157   virtual ::Window FindWindowFor(const gfx::Point& screen_point) OVERRIDE;
158   virtual void SendXClientEvent(::Window xid, XEvent* event) OVERRIDE;
159
160   // The XID of the window which initiated the drag.
161   ::Window source_xid_;
162
163   // The XID of the window which is simulated to be the topmost window at the
164   // current mouse position.
165   ::Window target_xid_;
166
167   // The move loop. Not owned.
168   TestMoveLoop* loop_;
169
170   // Map of ::Windows to the collector which intercepts XClientMessageEvents
171   // for that window.
172   std::map< ::Window, ClientMessageEventCollector*> collectors_;
173
174   ui::X11AtomCache atom_cache_;
175
176   DISALLOW_COPY_AND_ASSIGN(TestDragDropClient);
177 };
178
179 ///////////////////////////////////////////////////////////////////////////////
180 // ClientMessageEventCollector
181
182 ClientMessageEventCollector::ClientMessageEventCollector(
183     ::Window xid,
184     TestDragDropClient* client)
185     : xid_(xid),
186       client_(client) {
187   client->SetEventCollectorFor(xid, this);
188 }
189
190 ClientMessageEventCollector::~ClientMessageEventCollector() {
191   client_->SetEventCollectorFor(xid_, NULL);
192 }
193
194 std::vector<XClientMessageEvent> ClientMessageEventCollector::PopAllEvents() {
195   std::vector<XClientMessageEvent> to_return;
196   to_return.swap(events_);
197   return to_return;
198 }
199
200 void ClientMessageEventCollector::RecordEvent(
201     const XClientMessageEvent& event) {
202   events_.push_back(event);
203 }
204
205 ///////////////////////////////////////////////////////////////////////////////
206 // TestMoveLoop
207
208 TestMoveLoop::TestMoveLoop(X11MoveLoopDelegate* delegate)
209     : delegate_(delegate),
210       is_running_(false) {
211 }
212
213 TestMoveLoop::~TestMoveLoop() {
214 }
215
216 bool TestMoveLoop::IsRunning() const {
217   return is_running_;
218 }
219
220 bool TestMoveLoop::RunMoveLoop(
221     aura::Window* window,
222     gfx::NativeCursor cursor) {
223   is_running_ = true;
224   base::RunLoop run_loop;
225   quit_closure_ = run_loop.QuitClosure();
226   run_loop.Run();
227   return true;
228 }
229
230 void TestMoveLoop::UpdateCursor(gfx::NativeCursor cursor) {
231 }
232
233 void TestMoveLoop::EndMoveLoop() {
234   if (is_running_) {
235     delegate_->OnMoveLoopEnded();
236     is_running_ = false;
237     quit_closure_.Run();
238   }
239 }
240
241 ///////////////////////////////////////////////////////////////////////////////
242 // TestDragDropClient
243
244 // static
245 const int TestDragDropClient::kMouseMoveX = 100;
246
247 // static
248 const int TestDragDropClient::kMouseMoveY = 200;
249
250 TestDragDropClient::TestDragDropClient(
251     aura::Window* window,
252     DesktopNativeCursorManager* cursor_manager)
253     : DesktopDragDropClientAuraX11(window,
254                                    cursor_manager,
255                                    gfx::GetXDisplay(),
256                                    window->GetHost()->GetAcceleratedWidget()),
257       source_xid_(window->GetHost()->GetAcceleratedWidget()),
258       target_xid_(None),
259       loop_(NULL),
260       atom_cache_(gfx::GetXDisplay(), kAtomsToCache) {
261 }
262
263 TestDragDropClient::~TestDragDropClient() {
264 }
265
266 Atom TestDragDropClient::GetAtom(const char* name) {
267   return atom_cache_.GetAtom(name);
268 }
269
270 bool TestDragDropClient::MessageHasType(const XClientMessageEvent& event,
271                                         const char* type) {
272   return event.message_type == atom_cache_.GetAtom(type);
273 }
274
275 void TestDragDropClient::SetEventCollectorFor(
276     ::Window xid,
277     ClientMessageEventCollector* collector) {
278   if (collector)
279     collectors_[xid] = collector;
280   else
281     collectors_.erase(xid);
282 }
283
284 void TestDragDropClient::OnStatus(XID target_window,
285                                   bool will_accept_drop,
286                                   ::Atom accepted_action) {
287   XClientMessageEvent event;
288   event.message_type = atom_cache_.GetAtom("XdndStatus");
289   event.format = 32;
290   event.window = source_xid_;
291   event.data.l[0] = target_window;
292   event.data.l[1] = will_accept_drop ? 1 : 0;
293   event.data.l[2] = 0;
294   event.data.l[3] = 0;
295   event.data.l[4] = accepted_action;
296   OnXdndStatus(event);
297 }
298
299 void TestDragDropClient::OnFinished(XID target_window,
300                                     bool accepted_drop,
301                                     ::Atom performed_action) {
302   XClientMessageEvent event;
303   event.message_type = atom_cache_.GetAtom("XdndFinished");
304   event.format = 32;
305   event.window = source_xid_;
306   event.data.l[0] = target_window;
307   event.data.l[1] = accepted_drop ? 1 : 0;
308   event.data.l[2] = performed_action;
309   event.data.l[3] = 0;
310   event.data.l[4] = 0;
311   OnXdndFinished(event);
312 }
313
314 void TestDragDropClient::SetTopmostXWindowAndMoveMouse(::Window xid) {
315   target_xid_ = xid;
316
317   XMotionEvent event;
318   event.time = CurrentTime;
319   event.x_root = kMouseMoveX;
320   event.y_root = kMouseMoveY;
321   OnMouseMovement(&event);
322 }
323
324 bool TestDragDropClient::IsMoveLoopRunning() {
325   return loop_->IsRunning();
326 }
327
328 scoped_ptr<X11MoveLoop> TestDragDropClient::CreateMoveLoop(
329     X11MoveLoopDelegate* delegate) {
330   loop_ = new TestMoveLoop(delegate);
331   return scoped_ptr<X11MoveLoop>(loop_);
332 }
333
334 ::Window TestDragDropClient::FindWindowFor(const gfx::Point& screen_point) {
335   return target_xid_;
336 }
337
338 void TestDragDropClient::SendXClientEvent(::Window xid, XEvent* event) {
339   std::map< ::Window, ClientMessageEventCollector*>::iterator it =
340       collectors_.find(xid);
341   if (it != collectors_.end())
342     it->second->RecordEvent(event->xclient);
343 }
344
345 }  // namespace
346
347 class DesktopDragDropClientAuraX11Test : public ViewsTestBase {
348  public:
349   DesktopDragDropClientAuraX11Test() {
350   }
351
352   virtual ~DesktopDragDropClientAuraX11Test() {
353   }
354
355   int StartDragAndDrop() {
356     ui::OSExchangeData data;
357     data.SetString(base::ASCIIToUTF16("Test"));
358
359     return client_->StartDragAndDrop(
360         data,
361         widget_->GetNativeWindow()->GetRootWindow(),
362         widget_->GetNativeWindow(),
363         gfx::Point(),
364         ui::DragDropTypes::DRAG_COPY,
365         ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
366   }
367
368   // ViewsTestBase:
369   virtual void SetUp() OVERRIDE {
370     ViewsTestBase::SetUp();
371
372     // Create widget to initiate the drags.
373     widget_.reset(new Widget);
374     Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
375     params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
376     params.native_widget = new DesktopNativeWidgetAura(widget_.get());
377     params.bounds = gfx::Rect(100, 100);
378     widget_->Init(params);
379     widget_->Show();
380
381     cursor_manager_.reset(new DesktopNativeCursorManager(
382         DesktopCursorLoaderUpdater::Create()));
383
384     client_.reset(new TestDragDropClient(widget_->GetNativeWindow(),
385                                          cursor_manager_.get()));
386     client_->Init();
387   }
388
389   virtual void TearDown() OVERRIDE {
390     client_.reset();
391     cursor_manager_.reset();
392     widget_.reset();
393     ViewsTestBase::TearDown();
394   }
395
396   TestDragDropClient* client() {
397     return client_.get();
398   }
399
400  private:
401   scoped_ptr<TestDragDropClient> client_;
402   scoped_ptr<DesktopNativeCursorManager> cursor_manager_;
403
404   // The widget used to initiate drags.
405   scoped_ptr<Widget> widget_;
406
407   DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11Test);
408 };
409
410 namespace {
411
412 void BasicStep2(TestDragDropClient* client, XID toplevel) {
413   EXPECT_TRUE(client->IsMoveLoopRunning());
414
415   ClientMessageEventCollector collector(toplevel, client);
416   client->SetTopmostXWindowAndMoveMouse(toplevel);
417
418   // XdndEnter should have been sent to |toplevel| before the XdndPosition
419   // message.
420   std::vector<XClientMessageEvent> events = collector.PopAllEvents();
421   ASSERT_EQ(2u, events.size());
422
423   EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter"));
424   EXPECT_EQ(client->source_xwindow(),
425             static_cast<XID>(events[0].data.l[0]));
426   EXPECT_EQ(1, events[0].data.l[1] & 1);
427   std::vector<Atom> targets;
428   ui::GetAtomArrayProperty(client->source_xwindow(), "XdndTypeList", &targets);
429   EXPECT_FALSE(targets.empty());
430
431   EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition"));
432   EXPECT_EQ(client->source_xwindow(),
433             static_cast<XID>(events[0].data.l[0]));
434   const long kCoords =
435       TestDragDropClient::kMouseMoveX << 16 | TestDragDropClient::kMouseMoveY;
436   EXPECT_EQ(kCoords, events[1].data.l[2]);
437   EXPECT_EQ(client->GetAtom("XdndActionCopy"),
438             static_cast<Atom>(events[1].data.l[4]));
439
440   client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy"));
441
442   // Because there is no unprocessed XdndPosition, the drag drop client should
443   // send XdndDrop immediately after the mouse is released.
444   client->OnMouseReleased();
445
446   events = collector.PopAllEvents();
447   ASSERT_EQ(1u, events.size());
448   EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop"));
449   EXPECT_EQ(client->source_xwindow(),
450             static_cast<XID>(events[0].data.l[0]));
451
452   // Send XdndFinished to indicate that the drag drop client can cleanup any
453   // data related to this drag. The move loop should end only after the
454   // XdndFinished message was received.
455   EXPECT_TRUE(client->IsMoveLoopRunning());
456   client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy"));
457   EXPECT_FALSE(client->IsMoveLoopRunning());
458 }
459
460 void BasicStep3(TestDragDropClient* client, XID toplevel) {
461   EXPECT_TRUE(client->IsMoveLoopRunning());
462
463   ClientMessageEventCollector collector(toplevel, client);
464   client->SetTopmostXWindowAndMoveMouse(toplevel);
465
466   std::vector<XClientMessageEvent> events = collector.PopAllEvents();
467   ASSERT_EQ(2u, events.size());
468   EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter"));
469   EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition"));
470
471   client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy"));
472   client->SetTopmostXWindowAndMoveMouse(toplevel);
473   events = collector.PopAllEvents();
474   ASSERT_EQ(1u, events.size());
475   EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition"));
476
477   // We have not received an XdndStatus ack for the second XdndPosition message.
478   // Test that sending XdndDrop is delayed till the XdndStatus ack is received.
479   client->OnMouseReleased();
480   EXPECT_FALSE(collector.HasEvents());
481
482   client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy"));
483   events = collector.PopAllEvents();
484   ASSERT_EQ(1u, events.size());
485   EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop"));
486
487   EXPECT_TRUE(client->IsMoveLoopRunning());
488   client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy"));
489   EXPECT_FALSE(client->IsMoveLoopRunning());
490 }
491
492 }  // namespace
493
494 TEST_F(DesktopDragDropClientAuraX11Test, Basic) {
495   XID toplevel = 1;
496
497   base::MessageLoop::current()->PostTask(FROM_HERE,
498                                          base::Bind(&BasicStep2,
499                                          client(),
500                                          toplevel));
501   int result = StartDragAndDrop();
502   EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
503
504   // Do another drag and drop to test that the data is properly cleaned up as a
505   // result of the XdndFinished message.
506   base::MessageLoop::current()->PostTask(FROM_HERE,
507                                          base::Bind(&BasicStep3,
508                                          client(),
509                                          toplevel));
510   result = StartDragAndDrop();
511   EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
512 }
513
514 namespace {
515
516 void TargetDoesNotRespondStep2(TestDragDropClient* client) {
517   EXPECT_TRUE(client->IsMoveLoopRunning());
518
519   XID toplevel = 1;
520   ClientMessageEventCollector collector(toplevel, client);
521   client->SetTopmostXWindowAndMoveMouse(toplevel);
522
523   std::vector<XClientMessageEvent> events = collector.PopAllEvents();
524   ASSERT_EQ(2u, events.size());
525   EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter"));
526   EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition"));
527
528   client->OnMouseReleased();
529   events = collector.PopAllEvents();
530   ASSERT_EQ(1u, events.size());
531   EXPECT_TRUE(client->MessageHasType(events[0], "XdndLeave"));
532   EXPECT_FALSE(client->IsMoveLoopRunning());
533 }
534
535 }  // namespace
536
537 // Test that we do not wait for the target to send XdndStatus if we have not
538 // received any XdndStatus messages at all from the target. The Unity
539 // DNDCollectionWindow is an example of an XdndAware target which does not
540 // respond to XdndPosition messages at all.
541 TEST_F(DesktopDragDropClientAuraX11Test, TargetDoesNotRespond) {
542   base::MessageLoop::current()->PostTask(
543       FROM_HERE,
544       base::Bind(&TargetDoesNotRespondStep2, client()));
545   int result = StartDragAndDrop();
546   EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result);
547 }
548
549 namespace {
550
551 void QueuePositionStep2(TestDragDropClient* client) {
552   EXPECT_TRUE(client->IsMoveLoopRunning());
553
554   XID toplevel = 1;
555   ClientMessageEventCollector collector(toplevel, client);
556   client->SetTopmostXWindowAndMoveMouse(toplevel);
557   client->SetTopmostXWindowAndMoveMouse(toplevel);
558   client->SetTopmostXWindowAndMoveMouse(toplevel);
559
560   std::vector<XClientMessageEvent> events = collector.PopAllEvents();
561   ASSERT_EQ(2u, events.size());
562   EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter"));
563   EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition"));
564
565   client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy"));
566   events = collector.PopAllEvents();
567   ASSERT_EQ(1u, events.size());
568   EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition"));
569
570   client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy"));
571   EXPECT_FALSE(collector.HasEvents());
572
573   client->OnMouseReleased();
574   events = collector.PopAllEvents();
575   ASSERT_EQ(1u, events.size());
576   EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop"));
577
578   EXPECT_TRUE(client->IsMoveLoopRunning());
579   client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy"));
580   EXPECT_FALSE(client->IsMoveLoopRunning());
581 }
582
583 }  // namespace
584
585 // Test that XdndPosition messages are queued till the pending XdndPosition
586 // message is acked via an XdndStatus message.
587 TEST_F(DesktopDragDropClientAuraX11Test, QueuePosition) {
588   base::MessageLoop::current()->PostTask(
589       FROM_HERE,
590       base::Bind(&QueuePositionStep2, client()));
591   int result = StartDragAndDrop();
592   EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
593 }
594
595 namespace {
596
597 void TargetChangesStep2(TestDragDropClient* client) {
598   EXPECT_TRUE(client->IsMoveLoopRunning());
599
600   XID toplevel1 = 1;
601   ClientMessageEventCollector collector1(toplevel1, client);
602   client->SetTopmostXWindowAndMoveMouse(toplevel1);
603
604   std::vector<XClientMessageEvent> events1 = collector1.PopAllEvents();
605   ASSERT_EQ(2u, events1.size());
606   EXPECT_TRUE(client->MessageHasType(events1[0], "XdndEnter"));
607   EXPECT_TRUE(client->MessageHasType(events1[1], "XdndPosition"));
608
609   XID toplevel2 = 2;
610   ClientMessageEventCollector collector2(toplevel2, client);
611   client->SetTopmostXWindowAndMoveMouse(toplevel2);
612
613   // It is possible for |toplevel1| to send XdndStatus after the source has sent
614   // XdndLeave but before |toplevel1| has received the XdndLeave message. The
615   // XdndStatus message should be ignored.
616   client->OnStatus(toplevel1, true, client->GetAtom("XdndActionCopy"));
617   events1 = collector1.PopAllEvents();
618   ASSERT_EQ(1u, events1.size());
619   EXPECT_TRUE(client->MessageHasType(events1[0], "XdndLeave"));
620
621   std::vector<XClientMessageEvent> events2 = collector2.PopAllEvents();
622   ASSERT_EQ(2u, events2.size());
623   EXPECT_TRUE(client->MessageHasType(events2[0], "XdndEnter"));
624   EXPECT_TRUE(client->MessageHasType(events2[1], "XdndPosition"));
625
626   client->OnStatus(toplevel2, true, client->GetAtom("XdndActionCopy"));
627   client->OnMouseReleased();
628   events2 = collector2.PopAllEvents();
629   ASSERT_EQ(1u, events2.size());
630   EXPECT_TRUE(client->MessageHasType(events2[0], "XdndDrop"));
631
632   EXPECT_TRUE(client->IsMoveLoopRunning());
633   client->OnFinished(toplevel2, true, client->GetAtom("XdndActionCopy"));
634   EXPECT_FALSE(client->IsMoveLoopRunning());
635 }
636
637 }  // namespace
638
639 // Test the behavior when the target changes during a drag.
640 TEST_F(DesktopDragDropClientAuraX11Test, TargetChanges) {
641   base::MessageLoop::current()->PostTask(
642       FROM_HERE,
643       base::Bind(&TargetChangesStep2, client()));
644   int result = StartDragAndDrop();
645   EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result);
646 }
647
648 namespace {
649
650 void RejectAfterMouseReleaseStep2(TestDragDropClient* client) {
651   EXPECT_TRUE(client->IsMoveLoopRunning());
652
653   XID toplevel = 1;
654   ClientMessageEventCollector collector(toplevel, client);
655   client->SetTopmostXWindowAndMoveMouse(toplevel);
656
657   std::vector<XClientMessageEvent> events = collector.PopAllEvents();
658   ASSERT_EQ(2u, events.size());
659   EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter"));
660   EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition"));
661
662   client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy"));
663   EXPECT_FALSE(collector.HasEvents());
664
665   // Send another mouse move such that there is a pending XdndPosition.
666   client->SetTopmostXWindowAndMoveMouse(toplevel);
667   events = collector.PopAllEvents();
668   ASSERT_EQ(1u, events.size());
669   EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition"));
670
671   client->OnMouseReleased();
672   // Reject the drop.
673   client->OnStatus(toplevel, false, None);
674
675   events = collector.PopAllEvents();
676   ASSERT_EQ(1u, events.size());
677   EXPECT_TRUE(client->MessageHasType(events[0], "XdndLeave"));
678   EXPECT_FALSE(client->IsMoveLoopRunning());
679 }
680
681 void RejectAfterMouseReleaseStep3(TestDragDropClient* client) {
682   EXPECT_TRUE(client->IsMoveLoopRunning());
683
684   XID toplevel = 2;
685   ClientMessageEventCollector collector(toplevel, client);
686   client->SetTopmostXWindowAndMoveMouse(toplevel);
687
688   std::vector<XClientMessageEvent> events = collector.PopAllEvents();
689   ASSERT_EQ(2u, events.size());
690   EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter"));
691   EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition"));
692
693   client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy"));
694   EXPECT_FALSE(collector.HasEvents());
695
696   client->OnMouseReleased();
697   events = collector.PopAllEvents();
698   ASSERT_EQ(1u, events.size());
699   EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop"));
700
701   EXPECT_TRUE(client->IsMoveLoopRunning());
702   client->OnFinished(toplevel, false, None);
703   EXPECT_FALSE(client->IsMoveLoopRunning());
704 }
705
706 }  // namespace
707
708 // Test that the source sends XdndLeave instead of XdndDrop if the drag
709 // operation is rejected after the mouse is released.
710 TEST_F(DesktopDragDropClientAuraX11Test, RejectAfterMouseRelease) {
711   base::MessageLoop::current()->PostTask(
712       FROM_HERE,
713       base::Bind(&RejectAfterMouseReleaseStep2, client()));
714   int result = StartDragAndDrop();
715   EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result);
716
717   // Repeat the test but reject the drop in the XdndFinished message instead.
718   base::MessageLoop::current()->PostTask(
719       FROM_HERE,
720       base::Bind(&RejectAfterMouseReleaseStep3, client()));
721   result = StartDragAndDrop();
722   EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result);
723 }
724
725 }  // namespace views