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