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 30namespace views { 31 32namespace { 33 34const char* kAtomsToCache[] = { 35 "XdndActionCopy", 36 "XdndDrop", 37 "XdndEnter", 38 "XdndFinished", 39 "XdndLeave", 40 "XdndPosition", 41 "XdndStatus", 42 "XdndTypeList", 43 NULL 44}; 45 46class TestDragDropClient; 47 48// Collects messages which would otherwise be sent to |xid_| via 49// SendXClientEvent(). 50class 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. 80class 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_|. 106class 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 182ClientMessageEventCollector::ClientMessageEventCollector( 183 ::Window xid, 184 TestDragDropClient* client) 185 : xid_(xid), 186 client_(client) { 187 client->SetEventCollectorFor(xid, this); 188} 189 190ClientMessageEventCollector::~ClientMessageEventCollector() { 191 client_->SetEventCollectorFor(xid_, NULL); 192} 193 194std::vector<XClientMessageEvent> ClientMessageEventCollector::PopAllEvents() { 195 std::vector<XClientMessageEvent> to_return; 196 to_return.swap(events_); 197 return to_return; 198} 199 200void ClientMessageEventCollector::RecordEvent( 201 const XClientMessageEvent& event) { 202 events_.push_back(event); 203} 204 205/////////////////////////////////////////////////////////////////////////////// 206// TestMoveLoop 207 208TestMoveLoop::TestMoveLoop(X11MoveLoopDelegate* delegate) 209 : delegate_(delegate), 210 is_running_(false) { 211} 212 213TestMoveLoop::~TestMoveLoop() { 214} 215 216bool TestMoveLoop::IsRunning() const { 217 return is_running_; 218} 219 220bool 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 230void TestMoveLoop::UpdateCursor(gfx::NativeCursor cursor) { 231} 232 233void 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 245const int TestDragDropClient::kMouseMoveX = 100; 246 247// static 248const int TestDragDropClient::kMouseMoveY = 200; 249 250TestDragDropClient::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 263TestDragDropClient::~TestDragDropClient() { 264} 265 266Atom TestDragDropClient::GetAtom(const char* name) { 267 return atom_cache_.GetAtom(name); 268} 269 270bool TestDragDropClient::MessageHasType(const XClientMessageEvent& event, 271 const char* type) { 272 return event.message_type == atom_cache_.GetAtom(type); 273} 274 275void TestDragDropClient::SetEventCollectorFor( 276 ::Window xid, 277 ClientMessageEventCollector* collector) { 278 if (collector) 279 collectors_[xid] = collector; 280 else 281 collectors_.erase(xid); 282} 283 284void 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 299void 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 314void 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 324bool TestDragDropClient::IsMoveLoopRunning() { 325 return loop_->IsRunning(); 326} 327 328scoped_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 338void 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 347class 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 410namespace { 411 412void 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 460void 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 494TEST_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 514namespace { 515 516void 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. 541TEST_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 549namespace { 550 551void 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. 587TEST_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 595namespace { 596 597void 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. 640TEST_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 648namespace { 649 650void 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 681void 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. 710TEST_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