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