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