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 |