Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(296)

Side by Side Diff: services/ui/ws/current_drag_operation_unittest.cc

Issue 2266603002: mus: Implement interwindow drag and drop (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove stray mark Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 "services/ui/ws/current_drag_operation.h"
6 #include "services/ui/ws/current_drag_operation_source.h"
7 #include "services/ui/ws/drag_target_connection.h"
8 #include "services/ui/ws/ids.h"
9 #include "services/ui/ws/server_window.h"
10 #include "services/ui/ws/test_server_window_delegate.h"
11 #include "services/ui/ws/test_utils.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "ui/events/base_event_utils.h"
14
15 namespace ui {
16 namespace ws {
17
18 enum QueuedDragEventType {
19 TYPE_NONE,
20 TYPE_ENTER,
21 TYPE_OVER,
22 TYPE_LEAVE,
23 TYPE_DROP
24 };
25
26 class CurrentDragOperationTest;
27
28 // All the classes to represent a window.
29 class DragTestWindow : public DragTargetConnection {
30 public:
31 struct DragEvent {
32 QueuedDragEventType type;
33 uint32_t key_state;
34 gfx::Point cursor_offset;
35 uint32_t effect_bitmask;
36 base::Callback<void(uint32_t)> callback;
37 };
38
39 DragTestWindow(CurrentDragOperationTest* parent, const WindowId& id)
40 : parent_(parent), window_delegate_(), window_(&window_delegate_, id) {
41 window_.SetCanAcceptDrags(true);
42 }
43 ~DragTestWindow() override;
44
45 TestServerWindowDelegate* delegate() { return &window_delegate_; }
46 ServerWindow* window() { return &window_; }
47
48 QueuedDragEventType queue_response_type() {
49 if (queued_callbacks_.empty())
50 return TYPE_NONE;
51 return queued_callbacks_.front().type;
52 }
53
54 const DragEvent& queue_front() { return queued_callbacks_.front(); }
55
56 size_t queue_size() { return queued_callbacks_.size(); }
57
58 uint32_t times_received_drag_start() { return times_received_drag_start_; }
59
60 // Calls the callback at the front of the queue.
61 void Respond(bool respond_with_effect) {
62 if (queued_callbacks_.size()) {
63 if (!queued_callbacks_.front().callback.is_null()) {
64 queued_callbacks_.front().callback.Run(
65 respond_with_effect ? queued_callbacks_.front().effect_bitmask : 0);
66 }
67
68 queued_callbacks_.pop();
69 }
70 }
71
72 // Overridden from DragTestConnection:
73 void PerformOnDragStart(
74 const ServerWindow* window,
75 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data) override {
76 times_received_drag_start_++;
77 mime_data_ = std::move(mime_data);
78 }
79
80 void PerformOnDragEnter(
81 const ServerWindow* window,
82 uint32_t key_state,
83 const gfx::Point& cursor_offset,
84 uint32_t effect_bitmask,
85 const base::Callback<void(uint32_t)>& callback) override {
86 DCHECK_EQ(window, &window_);
87 queued_callbacks_.push(
88 {TYPE_ENTER, key_state, cursor_offset, effect_bitmask, callback});
89 }
90
91 void PerformOnDragOver(
92 const ServerWindow* window,
93 uint32_t key_state,
94 const gfx::Point& cursor_offset,
95 uint32_t effect_bitmask,
96 const base::Callback<void(uint32_t)>& callback) override {
97 DCHECK_EQ(window, &window_);
98 queued_callbacks_.push(
99 {TYPE_OVER, key_state, cursor_offset, effect_bitmask, callback});
100 }
101
102 void PerformOnDragLeave(const ServerWindow* window) override {
103 DCHECK_EQ(window, &window_);
104 queued_callbacks_.push(
105 {TYPE_LEAVE, 0, gfx::Point(), 0, base::Callback<void(uint32_t)>()});
106 }
107
108 void PerformOnDragDrop(
109 const ServerWindow* window,
110 uint32_t key_state,
111 const gfx::Point& cursor_offset,
112 uint32_t effect_bitmask,
113 const base::Callback<void(uint32_t)>& callback) override {
114 DCHECK_EQ(window, &window_);
115 queued_callbacks_.push(
116 {TYPE_DROP, key_state, cursor_offset, effect_bitmask, callback});
117 }
118
119 void PerformOnDragFinish(const ServerWindow* window) override {
120 mime_data_.SetToEmpty();
121 }
122
123 private:
124 CurrentDragOperationTest* parent_;
125 TestServerWindowDelegate window_delegate_;
126 ServerWindow window_;
127 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data_;
128 uint32_t times_received_drag_start_ = 0;
129
130 std::queue<DragEvent> queued_callbacks_;
131 };
132
133 class CurrentDragOperationTest : public testing::Test,
134 public CurrentDragOperationSource {
135 public:
136 std::unique_ptr<DragTestWindow> BuildWindow() {
137 WindowId id(1, ++window_id_);
138 std::unique_ptr<DragTestWindow> p =
139 base::MakeUnique<DragTestWindow>(this, id);
140 server_window_by_id_[id] = p->window();
141 connection_by_window_[p->window()] = p.get();
142 return p;
143 }
144
145 void StartDragOperation(
146 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data,
147 DragTestWindow* window,
148 uint32_t drag_operations) {
149 window->PerformOnDragStart(window->window(), mime_data.Clone());
150 drag_operation_.reset(new CurrentDragOperation(
151 this, window->window(), std::move(mime_data), drag_operations));
152 }
153
154 void DispatchDrag(DragTestWindow* window,
155 bool mouse_released,
156 uint32_t flags,
157 const gfx::Point& position) {
158 ui::PointerEvent event(
159 ui::MouseEvent(mouse_released ? ET_MOUSE_RELEASED : ET_MOUSE_PRESSED,
160 position, position, ui::EventTimeForNow(), flags, 0));
161 drag_operation_->DispatchLocatedEvent(event,
162 window ? window->window() : nullptr);
163 }
164
165 void OnTestWindowDestroyed(DragTestWindow* test_window) {
166 server_window_by_id_.erase(test_window->window()->id());
167 connection_by_window_.erase(test_window->window());
168 }
169
170 CurrentDragOperation* drag_operation() const { return drag_operation_.get(); }
171 const base::Optional<bool>& drag_over_value() { return drag_over_value_; }
172
173 private:
174 // Overridden from testing::Test:
175 void SetUp() override {
176 testing::Test::SetUp();
177
178 window_delegate_.reset(new TestServerWindowDelegate());
179 root_window_.reset(
180 new ServerWindow(window_delegate_.get(), WindowId(1, 2)));
181 window_delegate_->set_root_window(root_window_.get());
182 root_window_->SetVisible(true);
183 }
184
185 void TearDown() override {
186 drag_operation_.reset();
187 root_window_.reset();
188 window_delegate_.reset();
189
190 DCHECK(server_window_by_id_.empty());
191 DCHECK(connection_by_window_.empty());
192
193 testing::Test::TearDown();
194 }
195
196 // Overridden from CurrentDragOperationSource:
197 void OnDragOver(bool success) override { drag_over_value_ = success; }
198
199 ServerWindow* GetWindowById(const WindowId& id) override {
200 auto it = server_window_by_id_.find(id);
201 if (it == server_window_by_id_.end())
202 return nullptr;
203 return it->second;
204 }
205
206 DragTargetConnection* GetDragTargetWithRoot(
207 const ServerWindow* window) override {
208 auto it = connection_by_window_.find(const_cast<ServerWindow*>(window));
209 if (it == connection_by_window_.end())
210 return nullptr;
211 return it->second;
212 }
213
214 int window_id_ = 3;
215
216 std::map<WindowId, ServerWindow*> server_window_by_id_;
217 std::map<ServerWindow*, DragTargetConnection*> connection_by_window_;
218
219 std::unique_ptr<TestServerWindowDelegate> window_delegate_;
220 std::unique_ptr<ServerWindow> root_window_;
221
222 std::unique_ptr<CurrentDragOperation> drag_operation_;
223
224 base::Optional<bool> drag_over_value_;
225 };
226
227 DragTestWindow::~DragTestWindow() {
228 parent_->OnTestWindowDestroyed(this);
229 }
230
231 TEST_F(CurrentDragOperationTest, SimpleDragDrop) {
232 std::unique_ptr<DragTestWindow> window = BuildWindow();
233 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
234 StartDragOperation(std::move(mime_data), window.get(),
235 ui::mojom::kDropEffectMove);
236
237 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1));
238 EXPECT_EQ(TYPE_ENTER, window->queue_response_type());
239 window->Respond(true);
240
241 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(2, 2));
242 EXPECT_EQ(TYPE_OVER, window->queue_response_type());
243 window->Respond(true);
244
245 DispatchDrag(window.get(), true, 0, gfx::Point(2, 2));
246 EXPECT_EQ(TYPE_DROP, window->queue_response_type());
247 window->Respond(true);
248
249 EXPECT_TRUE(drag_over_value().value_or(false));
250 }
251
252 TEST_F(CurrentDragOperationTest, OnlyDeliverMimeDataOnce) {
253 std::unique_ptr<DragTestWindow> window1 = BuildWindow();
254 std::unique_ptr<DragTestWindow> window2 = BuildWindow();
255 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
256
257 // The client lib is responsible for sending the data to the window that's
258 // the drag source to minimize IPC.
259 EXPECT_EQ(0u, window1->times_received_drag_start());
260 StartDragOperation(std::move(mime_data), window1.get(),
261 ui::mojom::kDropEffectMove);
262 EXPECT_EQ(1u, window1->times_received_drag_start());
263 DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
264 gfx::Point(1, 1));
265 EXPECT_EQ(1u, window1->times_received_drag_start());
266 window1->Respond(true);
267
268 // Window2 doesn't receive the drag data until mouse is over it.
269 EXPECT_EQ(0u, window2->times_received_drag_start());
270 DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
271 gfx::Point(2, 2));
272 EXPECT_EQ(1u, window2->times_received_drag_start());
273
274 // Moving back to the source window doesn't send an additional start message.
275 DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
276 gfx::Point(1, 1));
277 EXPECT_EQ(1u, window1->times_received_drag_start());
278
279 // Moving back to window2 doesn't send an additional start message.
280 DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
281 gfx::Point(1, 1));
282 EXPECT_EQ(1u, window2->times_received_drag_start());
283 }
284
285 TEST_F(CurrentDragOperationTest, FailWhenDropOverNoWindow) {
286 std::unique_ptr<DragTestWindow> window = BuildWindow();
287 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
288 StartDragOperation(std::move(mime_data), window.get(),
289 ui::mojom::kDropEffectMove);
290
291 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1));
292 EXPECT_EQ(TYPE_ENTER, window->queue_response_type());
293 window->Respond(true);
294
295 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(2, 2));
296 EXPECT_EQ(TYPE_OVER, window->queue_response_type());
297 window->Respond(true);
298
299 DispatchDrag(nullptr, true, 0, gfx::Point(2, 2));
300 // Moving outside of |window| should result in |window| getting a leave.
301 EXPECT_EQ(TYPE_LEAVE, window->queue_response_type());
302 window->Respond(true);
303
304 EXPECT_FALSE(drag_over_value().value_or(true));
305 }
306
307 TEST_F(CurrentDragOperationTest, EnterLeaveWhenMovingBetweenTwoWindows) {
308 std::unique_ptr<DragTestWindow> window1 = BuildWindow();
309 std::unique_ptr<DragTestWindow> window2 = BuildWindow();
310 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
311 StartDragOperation(std::move(mime_data), window1.get(),
312 ui::mojom::kDropEffectMove);
313
314 DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
315 gfx::Point(1, 1));
316 EXPECT_EQ(TYPE_ENTER, window1->queue_response_type());
317 window1->Respond(true);
318
319 DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
320 gfx::Point(2, 2));
321 EXPECT_EQ(TYPE_ENTER, window2->queue_response_type());
322 EXPECT_EQ(TYPE_LEAVE, window1->queue_response_type());
323 window1->Respond(true);
324 window2->Respond(true);
325 }
326
327 TEST_F(CurrentDragOperationTest, EnterToOverQueued) {
328 std::unique_ptr<DragTestWindow> window = BuildWindow();
329 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
330 StartDragOperation(std::move(mime_data), window.get(),
331 ui::mojom::kDropEffectMove);
332
333 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1));
334 ASSERT_EQ(1u, window->queue_size());
335 EXPECT_EQ(TYPE_ENTER, window->queue_response_type());
336 // Don't respond.
337
338 // We don't receive another message since we haven't acknowledged the first.
339 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 2));
340 ASSERT_EQ(1u, window->queue_size());
341
342 // Responding causes us to receive our next event.
343 window->Respond(true);
344 ASSERT_EQ(1u, window->queue_size());
345 EXPECT_EQ(TYPE_OVER, window->queue_response_type());
346 }
347
348 TEST_F(CurrentDragOperationTest, CoalesceMouseOverEvents) {
349 std::unique_ptr<DragTestWindow> window = BuildWindow();
350 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
351 StartDragOperation(std::move(mime_data), window.get(),
352 ui::mojom::kDropEffectMove);
353
354 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1));
355 EXPECT_EQ(TYPE_ENTER, window->queue_response_type());
356
357 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 2));
358 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(2, 2));
359 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(2, 3));
360
361 // Responding to the first delivers us the last mouse over event's position.
362 window->Respond(true);
363 ASSERT_EQ(1u, window->queue_size());
364 EXPECT_EQ(TYPE_OVER, window->queue_response_type());
365 EXPECT_EQ(gfx::Point(2, 3), window->queue_front().cursor_offset);
366
367 // There are no queued events because they were coalesced.
368 window->Respond(true);
369 EXPECT_EQ(0u, window->queue_size());
370 }
371
372 TEST_F(CurrentDragOperationTest, RemovePendingMouseOversOnLeave) {
373 std::unique_ptr<DragTestWindow> window1 = BuildWindow();
374 std::unique_ptr<DragTestWindow> window2 = BuildWindow();
375 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
376 StartDragOperation(std::move(mime_data), window1.get(),
377 ui::mojom::kDropEffectMove);
378
379 // Enter
380 DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
381 gfx::Point(1, 1));
382 EXPECT_EQ(TYPE_ENTER, window1->queue_response_type());
383
384 // Over
385 DispatchDrag(window1.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
386 gfx::Point(1, 1));
387
388 // Leave
389 DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
390 gfx::Point(1, 1));
391
392 // The window finally responds to the enter message; we should not receive
393 // any over messages since we didn't respond to the enter message in time.
394 window1->Respond(true);
395 ASSERT_EQ(1u, window1->queue_size());
396 EXPECT_EQ(TYPE_LEAVE, window1->queue_response_type());
397 }
398
399 TEST_F(CurrentDragOperationTest, TargetWindowClosedWhileDrag) {
400 std::unique_ptr<DragTestWindow> window1 = BuildWindow();
401 std::unique_ptr<DragTestWindow> window2 = BuildWindow();
402 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
403 StartDragOperation(std::move(mime_data), window1.get(),
404 ui::mojom::kDropEffectMove);
405
406 test::CurrentDragOperationTestApi api(drag_operation());
407
408 // Send some events to |window|.
409 DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
410 gfx::Point(1, 1));
411 EXPECT_EQ(TYPE_ENTER, window2->queue_response_type());
412 DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
413 gfx::Point(1, 1));
414
415 ServerWindow* server_window = window2->window();
416
417 // Ensure that CurrentDragOperation is waiting for a response from |window|.
418 EXPECT_EQ(2u, api.GetSizeOfQueueForWindow(server_window));
419 EXPECT_EQ(server_window, api.GetCurrentTarget());
420
421 // Force the destruction of |window.window|.
422 window2.reset();
423
424 // CurrentDragOperation doesn't know anything about the server window now.
425 EXPECT_EQ(0u, api.GetSizeOfQueueForWindow(server_window));
426 EXPECT_EQ(nullptr, api.GetCurrentTarget());
427
428 // But a target window closing out from under us doesn't fail the drag.
429 EXPECT_FALSE(drag_over_value().has_value());
430 }
431
432 TEST_F(CurrentDragOperationTest, SourceWindowClosedWhileDrag) {
433 std::unique_ptr<DragTestWindow> window1 = BuildWindow();
434 std::unique_ptr<DragTestWindow> window2 = BuildWindow();
435 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
436 StartDragOperation(std::move(mime_data), window1.get(),
437 ui::mojom::kDropEffectMove);
438
439 test::CurrentDragOperationTestApi api(drag_operation());
440
441 // Send some events to |window|.
442 DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
443 gfx::Point(1, 1));
444 EXPECT_EQ(TYPE_ENTER, window2->queue_response_type());
445 DispatchDrag(window2.get(), false, ui::EF_LEFT_MOUSE_BUTTON,
446 gfx::Point(1, 1));
447
448 ServerWindow* server_window = window2->window();
449
450 // Ensure that CurrentDragOperation is waiting for a response from |window|.
451 EXPECT_EQ(2u, api.GetSizeOfQueueForWindow(server_window));
452 EXPECT_EQ(server_window, api.GetCurrentTarget());
453
454 // Force the destruction of the source window.
455 window1.reset();
456
457 // The source window going away fails the drag.
458 EXPECT_FALSE(drag_over_value().value_or(true));
459 }
460
461 TEST_F(CurrentDragOperationTest, DontQueueEventsAfterDrop) {
462 // The CurrentDragOperation needs to stick around to coordinate the drop, but
463 // it should ignore further mouse events during this time.
464 std::unique_ptr<DragTestWindow> window = BuildWindow();
465 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
466 StartDragOperation(std::move(mime_data), window.get(),
467 ui::mojom::kDropEffectMove);
468
469 test::CurrentDragOperationTestApi api(drag_operation());
470
471 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1));
472 EXPECT_EQ(TYPE_ENTER, window->queue_response_type());
473 window->Respond(true);
474
475 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(2, 2));
476 EXPECT_EQ(TYPE_OVER, window->queue_response_type());
477 window->Respond(true);
478
479 DispatchDrag(window.get(), true, 0, gfx::Point(2, 2));
480 EXPECT_EQ(TYPE_DROP, window->queue_response_type());
481 EXPECT_EQ(1u, api.GetSizeOfQueueForWindow(window->window()));
482
483 // Further located events don't result in additional drag messages.
484 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(2, 2));
485 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(2, 2));
486 EXPECT_EQ(1u, api.GetSizeOfQueueForWindow(window->window()));
487 }
488
489 TEST_F(CurrentDragOperationTest, CancelDrag) {
490 // The CurrentDragOperation needs to stick around to coordinate the drop, but
491 // it should ignore further mouse events during this time.
492 std::unique_ptr<DragTestWindow> window = BuildWindow();
493 mojo::Map<mojo::String, mojo::Array<uint8_t>> mime_data;
494 StartDragOperation(std::move(mime_data), window.get(),
495 ui::mojom::kDropEffectMove);
496
497 DispatchDrag(window.get(), false, ui::EF_LEFT_MOUSE_BUTTON, gfx::Point(1, 1));
498 EXPECT_EQ(TYPE_ENTER, window->queue_response_type());
499 window->Respond(true);
500
501 drag_operation()->DispatchCancel();
502
503 EXPECT_FALSE(drag_over_value().value_or(true));
504 }
505
506 // TODO(erg): Add a test to ensure windows that the cursor isn't over
507 // responding to messages don't change the cursor when we have cursor handling
508 // code.
509
510 } // namespace ws
511 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698