OLD | NEW |
(Empty) | |
| 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. |
| 54 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. |
| 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 |
| 162 ClientMessageEventCollector::ClientMessageEventCollector( |
| 163 ::Window xid, |
| 164 TestDragDropClient* client) |
| 165 : xid_(xid), |
| 166 client_(client) { |
| 167 client->SetEventCollectorFor(xid, this); |
| 168 } |
| 169 |
| 170 ClientMessageEventCollector::~ClientMessageEventCollector() { |
| 171 client_->SetEventCollectorFor(xid_, NULL); |
| 172 } |
| 173 |
| 174 std::vector<XClientMessageEvent> ClientMessageEventCollector::PopAllEvents() { |
| 175 std::vector<XClientMessageEvent> to_return; |
| 176 to_return.swap(events_); |
| 177 return to_return; |
| 178 } |
| 179 |
| 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 |
| 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 |
| 207 TestDragDropClient::~TestDragDropClient() { |
| 208 } |
| 209 |
| 210 Atom TestDragDropClient::GetAtom(const char* name) { |
| 211 return atom_cache_.GetAtom(name); |
| 212 } |
| 213 |
| 214 bool TestDragDropClient::MessageHasType(const XClientMessageEvent& event, |
| 215 const char* type) { |
| 216 return event.message_type == atom_cache_.GetAtom(type); |
| 217 } |
| 218 |
| 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 |
| 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 |
| 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 |
| 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 |
| 268 bool TestDragDropClient::IsMoveLoopRunning() { |
| 269 return move_loop_running_; |
| 270 } |
| 271 |
| 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 |
| 284 void TestDragDropClient::OnMoveLoopEnded() { |
| 285 DesktopDragDropClientAuraX11::OnMoveLoopEnded(); |
| 286 move_loop_running_ = false; |
| 287 } |
| 288 |
| 289 ::Window TestDragDropClient::FindWindowFor(const gfx::Point& screen_point) { |
| 290 return target_xid_; |
| 291 } |
| 292 |
| 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: |
| 304 DesktopDragDropClientAuraX11Test() { |
| 305 } |
| 306 |
| 307 virtual ~DesktopDragDropClientAuraX11Test() { |
| 308 } |
| 309 |
| 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: |
| 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 |
| 343 virtual void TearDown() OVERRIDE { |
| 344 client_.reset(); |
| 345 cursor_manager_.reset(); |
| 346 widget_.reset(); |
| 347 ViewsTestBase::TearDown(); |
| 348 } |
| 349 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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. |
| 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 |
| 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. |
| 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 |
| 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. |
| 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 |
| 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 |
| 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. |
| 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 |
OLD | NEW |