Home | History | Annotate | Download | only in desktop_aura
      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
    726