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