1// Copyright (c) 2012 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 "ash/drag_drop/drag_drop_controller.h"
6
7#include "ash/drag_drop/drag_drop_tracker.h"
8#include "ash/drag_drop/drag_image_view.h"
9#include "ash/shell.h"
10#include "ash/test/ash_test_base.h"
11#include "base/command_line.h"
12#include "base/location.h"
13#include "base/strings/utf_string_conversions.h"
14#include "ui/aura/client/capture_client.h"
15#include "ui/aura/window_event_dispatcher.h"
16#include "ui/aura/window_tree_host.h"
17#include "ui/base/clipboard/clipboard.h"
18#include "ui/base/clipboard/scoped_clipboard_writer.h"
19#include "ui/base/dragdrop/drag_drop_types.h"
20#include "ui/base/dragdrop/drag_utils.h"
21#include "ui/base/dragdrop/os_exchange_data.h"
22#include "ui/base/ui_base_switches.h"
23#include "ui/events/event.h"
24#include "ui/events/event_utils.h"
25#include "ui/events/gestures/gesture_types.h"
26#include "ui/events/test/event_generator.h"
27#include "ui/events/test/events_test_utils.h"
28#include "ui/gfx/animation/linear_animation.h"
29#include "ui/gfx/image/image_skia_rep.h"
30#include "ui/views/view.h"
31#include "ui/views/views_delegate.h"
32#include "ui/views/widget/native_widget_aura.h"
33#include "ui/views/widget/native_widget_delegate.h"
34#include "ui/views/widget/widget.h"
35
36namespace ash {
37namespace test {
38
39namespace {
40
41// A simple view that makes sure RunShellDrag is invoked on mouse drag.
42class DragTestView : public views::View {
43 public:
44  DragTestView() : views::View() {
45    Reset();
46  }
47
48  void Reset() {
49    num_drag_enters_ = 0;
50    num_drag_exits_ = 0;
51    num_drag_updates_ = 0;
52    num_drops_ = 0;
53    drag_done_received_ = false;
54    long_tap_received_ = false;
55  }
56
57  int VerticalDragThreshold() {
58    return views::View::GetVerticalDragThreshold();
59  }
60
61  int HorizontalDragThreshold() {
62    return views::View::GetHorizontalDragThreshold();
63  }
64
65  int num_drag_enters_;
66  int num_drag_exits_;
67  int num_drag_updates_;
68  int num_drops_;
69  bool drag_done_received_;
70  bool long_tap_received_;
71
72 private:
73  // View overrides:
74  virtual int GetDragOperations(const gfx::Point& press_pt) OVERRIDE {
75    return ui::DragDropTypes::DRAG_COPY;
76  }
77
78  virtual void WriteDragData(const gfx::Point& p,
79                             OSExchangeData* data) OVERRIDE {
80    data->SetString(base::UTF8ToUTF16("I am being dragged"));
81    gfx::ImageSkiaRep image_rep(gfx::Size(10, 20), 1.0f);
82    gfx::ImageSkia image_skia(image_rep);
83
84    drag_utils::SetDragImageOnDataObject(image_skia, gfx::Vector2d(), data);
85  }
86
87  virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
88    return true;
89  }
90
91  virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
92    if (event->type() == ui::ET_GESTURE_LONG_TAP)
93      long_tap_received_ = true;
94    return;
95  }
96
97  virtual bool GetDropFormats(
98      int* formats,
99      std::set<OSExchangeData::CustomFormat>* custom_formats) OVERRIDE {
100    *formats = ui::OSExchangeData::STRING;
101    return true;
102  }
103
104  virtual bool CanDrop(const OSExchangeData& data) OVERRIDE {
105    return true;
106  }
107
108  virtual void OnDragEntered(const ui::DropTargetEvent& event) OVERRIDE {
109    num_drag_enters_++;
110  }
111
112  virtual int OnDragUpdated(const ui::DropTargetEvent& event) OVERRIDE {
113    num_drag_updates_++;
114    return ui::DragDropTypes::DRAG_COPY;
115  }
116
117  virtual void OnDragExited() OVERRIDE {
118    num_drag_exits_++;
119  }
120
121  virtual int OnPerformDrop(const ui::DropTargetEvent& event) OVERRIDE {
122    num_drops_++;
123    return ui::DragDropTypes::DRAG_COPY;
124  }
125
126  virtual void OnDragDone() OVERRIDE {
127    drag_done_received_ = true;
128  }
129
130  DISALLOW_COPY_AND_ASSIGN(DragTestView);
131};
132
133class CompletableLinearAnimation : public gfx::LinearAnimation {
134 public:
135  CompletableLinearAnimation(int duration,
136                             int frame_rate,
137                             gfx::AnimationDelegate* delegate)
138      : gfx::LinearAnimation(duration, frame_rate, delegate),
139        duration_(duration) {
140  }
141
142  void Complete() {
143    Step(start_time() + base::TimeDelta::FromMilliseconds(duration_));
144  }
145
146 private:
147  int duration_;
148};
149
150class TestDragDropController : public DragDropController {
151 public:
152  TestDragDropController() : DragDropController() { Reset(); }
153
154  void Reset() {
155    drag_start_received_ = false;
156    num_drag_updates_ = 0;
157    drop_received_ = false;
158    drag_canceled_ = false;
159    drag_string_.clear();
160  }
161
162  virtual int StartDragAndDrop(
163      const ui::OSExchangeData& data,
164      aura::Window* root_window,
165      aura::Window* source_window,
166      const gfx::Point& location,
167      int operation,
168      ui::DragDropTypes::DragEventSource source) OVERRIDE {
169    drag_start_received_ = true;
170    data.GetString(&drag_string_);
171    return DragDropController::StartDragAndDrop(
172        data, root_window, source_window, location, operation, source);
173  }
174
175  virtual void DragUpdate(aura::Window* target,
176                          const ui::LocatedEvent& event) OVERRIDE {
177    DragDropController::DragUpdate(target, event);
178    num_drag_updates_++;
179  }
180
181  virtual void Drop(aura::Window* target,
182                    const ui::LocatedEvent& event) OVERRIDE {
183    DragDropController::Drop(target, event);
184    drop_received_ = true;
185  }
186
187  virtual void DragCancel() OVERRIDE {
188    DragDropController::DragCancel();
189    drag_canceled_ = true;
190  }
191
192  virtual gfx::LinearAnimation* CreateCancelAnimation(
193      int duration,
194      int frame_rate,
195      gfx::AnimationDelegate* delegate) OVERRIDE {
196    return new CompletableLinearAnimation(duration, frame_rate, delegate);
197  }
198
199  virtual void DoDragCancel(int animation_duration_ms) OVERRIDE {
200    DragDropController::DoDragCancel(animation_duration_ms);
201    drag_canceled_ = true;
202  }
203
204  bool drag_start_received_;
205  int num_drag_updates_;
206  bool drop_received_;
207  bool drag_canceled_;
208  base::string16 drag_string_;
209
210 private:
211  DISALLOW_COPY_AND_ASSIGN(TestDragDropController);
212};
213
214class TestNativeWidgetAura : public views::NativeWidgetAura {
215 public:
216  explicit TestNativeWidgetAura(views::internal::NativeWidgetDelegate* delegate)
217      : NativeWidgetAura(delegate),
218        check_if_capture_lost_(false) {
219  }
220
221  void set_check_if_capture_lost(bool value) {
222    check_if_capture_lost_ = value;
223  }
224
225  virtual void OnCaptureLost() OVERRIDE {
226    DCHECK(!check_if_capture_lost_);
227    views::NativeWidgetAura::OnCaptureLost();
228  }
229
230 private:
231  bool check_if_capture_lost_;
232
233  DISALLOW_COPY_AND_ASSIGN(TestNativeWidgetAura);
234};
235
236// TODO(sky): this is for debugging, remove when track down failure.
237void SetCheckIfCaptureLost(views::Widget* widget, bool value) {
238  // On Windows, the DCHECK triggers when running on bot or locally through RDP,
239  // but not when logged in locally.
240#if !defined(OS_WIN)
241  static_cast<TestNativeWidgetAura*>(widget->native_widget())->
242      set_check_if_capture_lost(value);
243#endif
244}
245
246views::Widget* CreateNewWidget() {
247  views::Widget* widget = new views::Widget;
248  views::Widget::InitParams params;
249  params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
250  params.accept_events = true;
251  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
252  params.parent = Shell::GetPrimaryRootWindow();
253  params.child = true;
254  params.native_widget = new TestNativeWidgetAura(widget);
255  widget->Init(params);
256  widget->Show();
257  return widget;
258}
259
260void AddViewToWidgetAndResize(views::Widget* widget, views::View* view) {
261  if (!widget->GetContentsView()) {
262    views::View* contents_view = new views::View;
263    widget->SetContentsView(contents_view);
264  }
265
266  views::View* contents_view = widget->GetContentsView();
267  contents_view->AddChildView(view);
268  view->SetBounds(contents_view->width(), 0, 100, 100);
269  gfx::Rect contents_view_bounds = contents_view->bounds();
270  contents_view_bounds.Union(view->bounds());
271  contents_view->SetBoundsRect(contents_view_bounds);
272  widget->SetBounds(contents_view_bounds);
273}
274
275void DispatchGesture(ui::EventType gesture_type, gfx::Point location) {
276  ui::GestureEventDetails event_details(gesture_type);
277  event_details.set_oldest_touch_id(1);
278  ui::GestureEvent gesture_event(
279      location.x(), location.y(), 0, ui::EventTimeForNow(), event_details);
280  ui::EventSource* event_source =
281      Shell::GetPrimaryRootWindow()->GetHost()->GetEventSource();
282  ui::EventSourceTestApi event_source_test(event_source);
283  ui::EventDispatchDetails details =
284      event_source_test.SendEventToProcessor(&gesture_event);
285  CHECK(!details.dispatcher_destroyed);
286}
287
288}  // namespace
289
290class DragDropControllerTest : public AshTestBase {
291 public:
292  DragDropControllerTest() : AshTestBase() {}
293  virtual ~DragDropControllerTest() {}
294
295  virtual void SetUp() OVERRIDE {
296    AshTestBase::SetUp();
297    drag_drop_controller_.reset(new TestDragDropController);
298    drag_drop_controller_->set_should_block_during_drag_drop(false);
299    aura::client::SetDragDropClient(Shell::GetPrimaryRootWindow(),
300                                    drag_drop_controller_.get());
301  }
302
303  virtual void TearDown() OVERRIDE {
304    aura::client::SetDragDropClient(Shell::GetPrimaryRootWindow(), NULL);
305    drag_drop_controller_.reset();
306    AshTestBase::TearDown();
307  }
308
309  void UpdateDragData(ui::OSExchangeData* data) {
310    drag_drop_controller_->drag_data_ = data;
311  }
312
313  aura::Window* GetDragWindow() {
314    return drag_drop_controller_->drag_window_;
315  }
316
317  aura::Window* GetDragSourceWindow() {
318    return drag_drop_controller_->drag_source_window_;
319  }
320
321  void SetDragSourceWindow(aura::Window* drag_source_window) {
322    drag_drop_controller_->drag_source_window_ = drag_source_window;
323    drag_source_window->AddObserver(drag_drop_controller_.get());
324  }
325
326  aura::Window* GetDragImageWindow() {
327    return drag_drop_controller_->drag_image_.get() ?
328        drag_drop_controller_->drag_image_->GetWidget()->GetNativeWindow() :
329        NULL;
330  }
331
332  DragDropTracker* drag_drop_tracker() {
333    return drag_drop_controller_->drag_drop_tracker_.get();
334  }
335
336  void CompleteCancelAnimation() {
337    CompletableLinearAnimation* animation =
338        static_cast<CompletableLinearAnimation*>(
339            drag_drop_controller_->cancel_animation_.get());
340    animation->Complete();
341  }
342
343 protected:
344  scoped_ptr<TestDragDropController> drag_drop_controller_;
345
346 private:
347  DISALLOW_COPY_AND_ASSIGN(DragDropControllerTest);
348};
349
350// TODO(win_aura) http://crbug.com/154081
351#if defined(OS_WIN)
352#define MAYBE_DragDropInSingleViewTest DISABLED_DragDropInSingleViewTest
353#else
354#define MAYBE_DragDropInSingleViewTest DragDropInSingleViewTest
355#endif
356TEST_F(DragDropControllerTest, MAYBE_DragDropInSingleViewTest) {
357  scoped_ptr<views::Widget> widget(CreateNewWidget());
358  DragTestView* drag_view = new DragTestView;
359  AddViewToWidgetAndResize(widget.get(), drag_view);
360  ui::OSExchangeData data;
361  data.SetString(base::UTF8ToUTF16("I am being dragged"));
362  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
363                                     widget->GetNativeView());
364  generator.PressLeftButton();
365
366  int num_drags = 17;
367  SetCheckIfCaptureLost(widget.get(), true);
368  for (int i = 0; i < num_drags; ++i) {
369    // Because we are not doing a blocking drag and drop, the original
370    // OSDragExchangeData object is lost as soon as we return from the drag
371    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
372    // drag_data_ to a fake drag data object that we created.
373    if (i > 0)
374      UpdateDragData(&data);
375    // 7 comes from views::View::GetVerticalDragThreshold()).
376    if (i >= 7)
377      SetCheckIfCaptureLost(widget.get(), false);
378
379    generator.MoveMouseBy(0, 1);
380
381    // Execute any scheduled draws to process deferred mouse events.
382    RunAllPendingInMessageLoop();
383  }
384
385  generator.ReleaseLeftButton();
386
387  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
388  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
389      drag_drop_controller_->num_drag_updates_);
390  EXPECT_TRUE(drag_drop_controller_->drop_received_);
391  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
392      drag_drop_controller_->drag_string_);
393
394  EXPECT_EQ(1, drag_view->num_drag_enters_);
395  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
396      drag_view->num_drag_updates_);
397  EXPECT_EQ(1, drag_view->num_drops_);
398  EXPECT_EQ(0, drag_view->num_drag_exits_);
399  EXPECT_TRUE(drag_view->drag_done_received_);
400}
401
402TEST_F(DragDropControllerTest, DragDropWithZeroDragUpdates) {
403  scoped_ptr<views::Widget> widget(CreateNewWidget());
404  DragTestView* drag_view = new DragTestView;
405  AddViewToWidgetAndResize(widget.get(), drag_view);
406  ui::OSExchangeData data;
407  data.SetString(base::UTF8ToUTF16("I am being dragged"));
408  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
409                                     widget->GetNativeView());
410  generator.PressLeftButton();
411
412  int num_drags = drag_view->VerticalDragThreshold() + 1;
413  for (int i = 0; i < num_drags; ++i) {
414    // Because we are not doing a blocking drag and drop, the original
415    // OSDragExchangeData object is lost as soon as we return from the drag
416    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
417    // drag_data_ to a fake drag data object that we created.
418    if (i > 0)
419      UpdateDragData(&data);
420    generator.MoveMouseBy(0, 1);
421  }
422
423  UpdateDragData(&data);
424
425  generator.ReleaseLeftButton();
426
427  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
428  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold() + 1,
429      drag_drop_controller_->num_drag_updates_);
430  EXPECT_TRUE(drag_drop_controller_->drop_received_);
431
432  EXPECT_EQ(1, drag_view->num_drag_enters_);
433  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold() + 1,
434      drag_view->num_drag_updates_);
435  EXPECT_EQ(1, drag_view->num_drops_);
436  EXPECT_EQ(0, drag_view->num_drag_exits_);
437  EXPECT_TRUE(drag_view->drag_done_received_);
438}
439
440// TODO(win_aura) http://crbug.com/154081
441#if defined(OS_WIN)
442#define MAYBE_DragDropInMultipleViewsSingleWidgetTest DISABLED_DragDropInMultipleViewsSingleWidgetTest
443#else
444#define MAYBE_DragDropInMultipleViewsSingleWidgetTest DragDropInMultipleViewsSingleWidgetTest
445#endif
446TEST_F(DragDropControllerTest, MAYBE_DragDropInMultipleViewsSingleWidgetTest) {
447  scoped_ptr<views::Widget> widget(CreateNewWidget());
448  DragTestView* drag_view1 = new DragTestView;
449  AddViewToWidgetAndResize(widget.get(), drag_view1);
450  DragTestView* drag_view2 = new DragTestView;
451  AddViewToWidgetAndResize(widget.get(), drag_view2);
452
453  ui::OSExchangeData data;
454  data.SetString(base::UTF8ToUTF16("I am being dragged"));
455
456  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
457  generator.MoveMouseRelativeTo(widget->GetNativeView(),
458                                drag_view1->bounds().CenterPoint());
459  generator.PressLeftButton();
460
461  int num_drags = drag_view1->width();
462  for (int i = 0; i < num_drags; ++i) {
463    // Because we are not doing a blocking drag and drop, the original
464    // OSDragExchangeData object is lost as soon as we return from the drag
465    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
466    // drag_data_ to a fake drag data object that we created.
467    if (i > 0)
468      UpdateDragData(&data);
469    generator.MoveMouseBy(1, 0);
470
471    // Execute any scheduled draws to process deferred mouse events.
472    RunAllPendingInMessageLoop();
473  }
474
475  generator.ReleaseLeftButton();
476
477  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
478  EXPECT_EQ(num_drags - 1 - drag_view1->HorizontalDragThreshold(),
479      drag_drop_controller_->num_drag_updates_);
480  EXPECT_TRUE(drag_drop_controller_->drop_received_);
481  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
482      drag_drop_controller_->drag_string_);
483
484  EXPECT_EQ(1, drag_view1->num_drag_enters_);
485  int num_expected_updates = drag_view1->bounds().width() -
486      drag_view1->bounds().CenterPoint().x() - 2;
487  EXPECT_EQ(num_expected_updates - drag_view1->HorizontalDragThreshold(),
488      drag_view1->num_drag_updates_);
489  EXPECT_EQ(0, drag_view1->num_drops_);
490  EXPECT_EQ(1, drag_view1->num_drag_exits_);
491  EXPECT_TRUE(drag_view1->drag_done_received_);
492
493  EXPECT_EQ(1, drag_view2->num_drag_enters_);
494  num_expected_updates = num_drags - num_expected_updates - 1;
495  EXPECT_EQ(num_expected_updates, drag_view2->num_drag_updates_);
496  EXPECT_EQ(1, drag_view2->num_drops_);
497  EXPECT_EQ(0, drag_view2->num_drag_exits_);
498  EXPECT_FALSE(drag_view2->drag_done_received_);
499}
500
501// TODO(win_aura) http://crbug.com/154081
502#if defined(OS_WIN)
503#define MAYBE_DragDropInMultipleViewsMultipleWidgetsTest DISABLED_DragDropInMultipleViewsMultipleWidgetsTest
504#else
505#define MAYBE_DragDropInMultipleViewsMultipleWidgetsTest DragDropInMultipleViewsMultipleWidgetsTest
506#endif
507TEST_F(DragDropControllerTest, MAYBE_DragDropInMultipleViewsMultipleWidgetsTest) {
508  scoped_ptr<views::Widget> widget1(CreateNewWidget());
509  DragTestView* drag_view1 = new DragTestView;
510  AddViewToWidgetAndResize(widget1.get(), drag_view1);
511  scoped_ptr<views::Widget> widget2(CreateNewWidget());
512  DragTestView* drag_view2 = new DragTestView;
513  AddViewToWidgetAndResize(widget2.get(), drag_view2);
514  gfx::Rect widget1_bounds = widget1->GetClientAreaBoundsInScreen();
515  gfx::Rect widget2_bounds = widget2->GetClientAreaBoundsInScreen();
516  widget2->SetBounds(gfx::Rect(widget1_bounds.width(), 0,
517      widget2_bounds.width(), widget2_bounds.height()));
518
519  ui::OSExchangeData data;
520  data.SetString(base::UTF8ToUTF16("I am being dragged"));
521
522  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
523                                     widget1->GetNativeView());
524  generator.PressLeftButton();
525
526  int num_drags = drag_view1->width();
527  for (int i = 0; i < num_drags; ++i) {
528    // Because we are not doing a blocking drag and drop, the original
529    // OSDragExchangeData object is lost as soon as we return from the drag
530    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
531    // drag_data_ to a fake drag data object that we created.
532    if (i > 0)
533      UpdateDragData(&data);
534    generator.MoveMouseBy(1, 0);
535
536    // Execute any scheduled draws to process deferred mouse events.
537    RunAllPendingInMessageLoop();
538  }
539
540  generator.ReleaseLeftButton();
541
542  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
543  EXPECT_EQ(num_drags - 1 - drag_view1->HorizontalDragThreshold(),
544      drag_drop_controller_->num_drag_updates_);
545  EXPECT_TRUE(drag_drop_controller_->drop_received_);
546  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
547      drag_drop_controller_->drag_string_);
548
549  EXPECT_EQ(1, drag_view1->num_drag_enters_);
550  int num_expected_updates = drag_view1->bounds().width() -
551      drag_view1->bounds().CenterPoint().x() - 2;
552  EXPECT_EQ(num_expected_updates - drag_view1->HorizontalDragThreshold(),
553      drag_view1->num_drag_updates_);
554  EXPECT_EQ(0, drag_view1->num_drops_);
555  EXPECT_EQ(1, drag_view1->num_drag_exits_);
556  EXPECT_TRUE(drag_view1->drag_done_received_);
557
558  EXPECT_EQ(1, drag_view2->num_drag_enters_);
559  num_expected_updates = num_drags - num_expected_updates - 1;
560  EXPECT_EQ(num_expected_updates, drag_view2->num_drag_updates_);
561  EXPECT_EQ(1, drag_view2->num_drops_);
562  EXPECT_EQ(0, drag_view2->num_drag_exits_);
563  EXPECT_FALSE(drag_view2->drag_done_received_);
564}
565
566// TODO(win_aura) http://crbug.com/154081
567#if defined(OS_WIN)
568#define MAYBE_ViewRemovedWhileInDragDropTest DISABLED_ViewRemovedWhileInDragDropTest
569#else
570#define MAYBE_ViewRemovedWhileInDragDropTest ViewRemovedWhileInDragDropTest
571#endif
572TEST_F(DragDropControllerTest, MAYBE_ViewRemovedWhileInDragDropTest) {
573  scoped_ptr<views::Widget> widget(CreateNewWidget());
574  scoped_ptr<DragTestView> drag_view(new DragTestView);
575  AddViewToWidgetAndResize(widget.get(), drag_view.get());
576  gfx::Point point = gfx::Rect(drag_view->bounds()).CenterPoint();
577  ui::OSExchangeData data;
578  data.SetString(base::UTF8ToUTF16("I am being dragged"));
579
580  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
581  generator.MoveMouseToCenterOf(widget->GetNativeView());
582  generator.PressLeftButton();
583
584  int num_drags_1 = 17;
585  for (int i = 0; i < num_drags_1; ++i) {
586    // Because we are not doing a blocking drag and drop, the original
587    // OSDragExchangeData object is lost as soon as we return from the drag
588    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
589    // drag_data_ to a fake drag data object that we created.
590    if (i > 0)
591      UpdateDragData(&data);
592    generator.MoveMouseBy(0, 1);
593
594    // Execute any scheduled draws to process deferred mouse events.
595    RunAllPendingInMessageLoop();
596  }
597
598  drag_view->parent()->RemoveChildView(drag_view.get());
599  // View has been removed. We will not get any of the following drag updates.
600  int num_drags_2 = 23;
601  for (int i = 0; i < num_drags_2; ++i) {
602    UpdateDragData(&data);
603    generator.MoveMouseBy(0, 1);
604
605    // Execute any scheduled draws to process deferred mouse events.
606    RunAllPendingInMessageLoop();
607  }
608
609  generator.ReleaseLeftButton();
610
611  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
612  EXPECT_EQ(num_drags_1 + num_drags_2 - 1 - drag_view->VerticalDragThreshold(),
613      drag_drop_controller_->num_drag_updates_);
614  EXPECT_TRUE(drag_drop_controller_->drop_received_);
615  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
616      drag_drop_controller_->drag_string_);
617
618  EXPECT_EQ(1, drag_view->num_drag_enters_);
619  EXPECT_EQ(num_drags_1 - 1 - drag_view->VerticalDragThreshold(),
620      drag_view->num_drag_updates_);
621  EXPECT_EQ(0, drag_view->num_drops_);
622  EXPECT_EQ(0, drag_view->num_drag_exits_);
623  EXPECT_TRUE(drag_view->drag_done_received_);
624}
625
626TEST_F(DragDropControllerTest, DragLeavesClipboardAloneTest) {
627  ui::Clipboard* cb = ui::Clipboard::GetForCurrentThread();
628  std::string clip_str("I am on the clipboard");
629  {
630    // We first copy some text to the clipboard.
631    ui::ScopedClipboardWriter scw(ui::CLIPBOARD_TYPE_COPY_PASTE);
632    scw.WriteText(base::ASCIIToUTF16(clip_str));
633  }
634  EXPECT_TRUE(cb->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(),
635      ui::CLIPBOARD_TYPE_COPY_PASTE));
636
637  scoped_ptr<views::Widget> widget(CreateNewWidget());
638  DragTestView* drag_view = new DragTestView;
639  AddViewToWidgetAndResize(widget.get(), drag_view);
640
641  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
642                                     widget->GetNativeView());
643  ui::OSExchangeData data;
644  std::string data_str("I am being dragged");
645  data.SetString(base::ASCIIToUTF16(data_str));
646
647  generator.PressLeftButton();
648  generator.MoveMouseBy(0, drag_view->VerticalDragThreshold() + 1);
649
650  // Execute any scheduled draws to process deferred mouse events.
651  RunAllPendingInMessageLoop();
652
653  // Verify the clipboard contents haven't changed
654  std::string result;
655  EXPECT_TRUE(cb->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(),
656      ui::CLIPBOARD_TYPE_COPY_PASTE));
657  cb->ReadAsciiText(ui::CLIPBOARD_TYPE_COPY_PASTE, &result);
658  EXPECT_EQ(clip_str, result);
659  // Destory the clipboard here because ash doesn't delete it.
660  // crbug.com/158150.
661  ui::Clipboard::DestroyClipboardForCurrentThread();
662}
663
664TEST_F(DragDropControllerTest, WindowDestroyedDuringDragDrop) {
665  scoped_ptr<views::Widget> widget(CreateNewWidget());
666  DragTestView* drag_view = new DragTestView;
667  AddViewToWidgetAndResize(widget.get(), drag_view);
668  aura::Window* window = widget->GetNativeView();
669
670  ui::OSExchangeData data;
671  data.SetString(base::UTF8ToUTF16("I am being dragged"));
672  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
673                                     widget->GetNativeView());
674  generator.PressLeftButton();
675
676  int num_drags = 17;
677  for (int i = 0; i < num_drags; ++i) {
678    // Because we are not doing a blocking drag and drop, the original
679    // OSDragExchangeData object is lost as soon as we return from the drag
680    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
681    // drag_data_ to a fake drag data object that we created.
682    if (i > 0)
683      UpdateDragData(&data);
684    generator.MoveMouseBy(0, 1);
685
686    // Execute any scheduled draws to process deferred mouse events.
687    RunAllPendingInMessageLoop();
688
689    if (i > drag_view->VerticalDragThreshold())
690      EXPECT_EQ(window, GetDragWindow());
691  }
692
693  widget->CloseNow();
694  EXPECT_FALSE(GetDragWindow());
695
696  num_drags = 23;
697  for (int i = 0; i < num_drags; ++i) {
698    if (i > 0)
699      UpdateDragData(&data);
700    generator.MoveMouseBy(0, 1);
701    // We should not crash here.
702  }
703
704  generator.ReleaseLeftButton();
705
706  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
707  EXPECT_TRUE(drag_drop_controller_->drop_received_);
708}
709
710TEST_F(DragDropControllerTest, SyntheticEventsDuringDragDrop) {
711  scoped_ptr<views::Widget> widget(CreateNewWidget());
712  DragTestView* drag_view = new DragTestView;
713  AddViewToWidgetAndResize(widget.get(), drag_view);
714  ui::OSExchangeData data;
715  data.SetString(base::UTF8ToUTF16("I am being dragged"));
716  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
717                                     widget->GetNativeView());
718  generator.PressLeftButton();
719
720  int num_drags = 17;
721  for (int i = 0; i < num_drags; ++i) {
722    // Because we are not doing a blocking drag and drop, the original
723    // OSDragExchangeData object is lost as soon as we return from the drag
724    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
725    // drag_data_ to a fake drag data object that we created.
726    if (i > 0)
727      UpdateDragData(&data);
728    generator.MoveMouseBy(0, 1);
729
730    // We send a unexpected mouse move event. Note that we cannot use
731    // EventGenerator since it implicitly turns these into mouse drag events.
732    // The DragDropController should simply ignore these events.
733    gfx::Point mouse_move_location = drag_view->bounds().CenterPoint();
734    ui::MouseEvent mouse_move(ui::ET_MOUSE_MOVED, mouse_move_location,
735                              mouse_move_location, 0, 0);
736    ui::EventDispatchDetails details = Shell::GetPrimaryRootWindow()->
737        GetHost()->event_processor()->OnEventFromSource(&mouse_move);
738    ASSERT_FALSE(details.dispatcher_destroyed);
739  }
740
741  generator.ReleaseLeftButton();
742
743  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
744  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
745      drag_drop_controller_->num_drag_updates_);
746  EXPECT_TRUE(drag_drop_controller_->drop_received_);
747  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
748      drag_drop_controller_->drag_string_);
749
750  EXPECT_EQ(1, drag_view->num_drag_enters_);
751  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
752      drag_view->num_drag_updates_);
753  EXPECT_EQ(1, drag_view->num_drops_);
754  EXPECT_EQ(0, drag_view->num_drag_exits_);
755  EXPECT_TRUE(drag_view->drag_done_received_);
756}
757
758// TODO(win_aura) http://crbug.com/154081
759#if defined(OS_WIN)
760#define MAYBE_PressingEscapeCancelsDragDrop DISABLED_PressingEscapeCancelsDragDrop
761#define MAYBE_CaptureLostCancelsDragDrop DISABLED_CaptureLostCancelsDragDrop
762#else
763#define MAYBE_PressingEscapeCancelsDragDrop PressingEscapeCancelsDragDrop
764#define MAYBE_CaptureLostCancelsDragDrop CaptureLostCancelsDragDrop
765#endif
766TEST_F(DragDropControllerTest, MAYBE_PressingEscapeCancelsDragDrop) {
767  scoped_ptr<views::Widget> widget(CreateNewWidget());
768  DragTestView* drag_view = new DragTestView;
769  AddViewToWidgetAndResize(widget.get(), drag_view);
770  ui::OSExchangeData data;
771  data.SetString(base::UTF8ToUTF16("I am being dragged"));
772  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
773                                     widget->GetNativeView());
774  generator.PressLeftButton();
775
776  int num_drags = 17;
777  for (int i = 0; i < num_drags; ++i) {
778    // Because we are not doing a blocking drag and drop, the original
779    // OSDragExchangeData object is lost as soon as we return from the drag
780    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
781    // drag_data_ to a fake drag data object that we created.
782    if (i > 0)
783      UpdateDragData(&data);
784    generator.MoveMouseBy(0, 1);
785
786    // Execute any scheduled draws to process deferred mouse events.
787    RunAllPendingInMessageLoop();
788  }
789
790  generator.PressKey(ui::VKEY_ESCAPE, 0);
791
792  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
793  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
794      drag_drop_controller_->num_drag_updates_);
795  EXPECT_FALSE(drag_drop_controller_->drop_received_);
796  EXPECT_TRUE(drag_drop_controller_->drag_canceled_);
797  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
798      drag_drop_controller_->drag_string_);
799
800  EXPECT_EQ(1, drag_view->num_drag_enters_);
801  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
802      drag_view->num_drag_updates_);
803  EXPECT_EQ(0, drag_view->num_drops_);
804  EXPECT_EQ(1, drag_view->num_drag_exits_);
805  EXPECT_TRUE(drag_view->drag_done_received_);
806}
807
808TEST_F(DragDropControllerTest, MAYBE_CaptureLostCancelsDragDrop) {
809  scoped_ptr<views::Widget> widget(CreateNewWidget());
810  DragTestView* drag_view = new DragTestView;
811  AddViewToWidgetAndResize(widget.get(), drag_view);
812  ui::OSExchangeData data;
813  data.SetString(base::UTF8ToUTF16("I am being dragged"));
814  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
815                                     widget->GetNativeView());
816  generator.PressLeftButton();
817
818  int num_drags = 17;
819  for (int i = 0; i < num_drags; ++i) {
820    // Because we are not doing a blocking drag and drop, the original
821    // OSDragExchangeData object is lost as soon as we return from the drag
822    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
823    // drag_data_ to a fake drag data object that we created.
824    if (i > 0)
825      UpdateDragData(&data);
826    generator.MoveMouseBy(0, 1);
827
828    // Execute any scheduled draws to process deferred mouse events.
829    RunAllPendingInMessageLoop();
830  }
831  // Make sure the capture window won't handle mouse events.
832  aura::Window* capture_window = drag_drop_tracker()->capture_window();
833  ASSERT_TRUE(!!capture_window);
834  EXPECT_EQ("0x0", capture_window->bounds().size().ToString());
835  EXPECT_EQ(NULL,
836            capture_window->GetEventHandlerForPoint(gfx::Point()));
837  EXPECT_EQ(NULL,
838            capture_window->GetTopWindowContainingPoint(gfx::Point()));
839
840  aura::client::GetCaptureClient(widget->GetNativeView()->GetRootWindow())->
841      SetCapture(NULL);
842
843  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
844  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
845      drag_drop_controller_->num_drag_updates_);
846  EXPECT_FALSE(drag_drop_controller_->drop_received_);
847  EXPECT_TRUE(drag_drop_controller_->drag_canceled_);
848  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
849      drag_drop_controller_->drag_string_);
850
851  EXPECT_EQ(1, drag_view->num_drag_enters_);
852  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
853      drag_view->num_drag_updates_);
854  EXPECT_EQ(0, drag_view->num_drops_);
855  EXPECT_EQ(1, drag_view->num_drag_exits_);
856  EXPECT_TRUE(drag_view->drag_done_received_);
857}
858
859TEST_F(DragDropControllerTest, TouchDragDropInMultipleWindows) {
860  CommandLine::ForCurrentProcess()->AppendSwitch(
861      switches::kEnableTouchDragDrop);
862  scoped_ptr<views::Widget> widget1(CreateNewWidget());
863  DragTestView* drag_view1 = new DragTestView;
864  AddViewToWidgetAndResize(widget1.get(), drag_view1);
865  scoped_ptr<views::Widget> widget2(CreateNewWidget());
866  DragTestView* drag_view2 = new DragTestView;
867  AddViewToWidgetAndResize(widget2.get(), drag_view2);
868  gfx::Rect widget1_bounds = widget1->GetClientAreaBoundsInScreen();
869  gfx::Rect widget2_bounds = widget2->GetClientAreaBoundsInScreen();
870  widget2->SetBounds(gfx::Rect(widget1_bounds.width(), 0,
871      widget2_bounds.width(), widget2_bounds.height()));
872
873  ui::OSExchangeData data;
874  data.SetString(base::UTF8ToUTF16("I am being dragged"));
875
876  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
877                                     widget1->GetNativeView());
878  generator.PressTouch();
879  gfx::Point point = gfx::Rect(drag_view1->bounds()).CenterPoint();
880  DispatchGesture(ui::ET_GESTURE_LONG_PRESS, point);
881  // Because we are not doing a blocking drag and drop, the original
882  // OSDragExchangeData object is lost as soon as we return from the drag
883  // initiation in DragDropController::StartDragAndDrop(). Hence we set the
884  // drag_data_ to a fake drag data object that we created.
885  UpdateDragData(&data);
886  gfx::Point gesture_location = point;
887  int num_drags = drag_view1->width();
888  for (int i = 0; i < num_drags; ++i) {
889    gesture_location.Offset(1, 0);
890    DispatchGesture(ui::ET_GESTURE_SCROLL_UPDATE, gesture_location);
891
892    // Execute any scheduled draws to process deferred mouse events.
893    RunAllPendingInMessageLoop();
894  }
895
896  DispatchGesture(ui::ET_GESTURE_SCROLL_END, gesture_location);
897
898  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
899  EXPECT_EQ(num_drags, drag_drop_controller_->num_drag_updates_);
900  EXPECT_TRUE(drag_drop_controller_->drop_received_);
901  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
902      drag_drop_controller_->drag_string_);
903
904  EXPECT_EQ(1, drag_view1->num_drag_enters_);
905  int num_expected_updates = drag_view1->bounds().width() -
906      drag_view1->bounds().CenterPoint().x() - 1;
907  EXPECT_EQ(num_expected_updates, drag_view1->num_drag_updates_);
908  EXPECT_EQ(0, drag_view1->num_drops_);
909  EXPECT_EQ(1, drag_view1->num_drag_exits_);
910  EXPECT_TRUE(drag_view1->drag_done_received_);
911
912  EXPECT_EQ(1, drag_view2->num_drag_enters_);
913  num_expected_updates = num_drags - num_expected_updates;
914  EXPECT_EQ(num_expected_updates, drag_view2->num_drag_updates_);
915  EXPECT_EQ(1, drag_view2->num_drops_);
916  EXPECT_EQ(0, drag_view2->num_drag_exits_);
917  EXPECT_FALSE(drag_view2->drag_done_received_);
918}
919
920TEST_F(DragDropControllerTest, TouchDragDropCancelsOnLongTap) {
921  CommandLine::ForCurrentProcess()->AppendSwitch(
922      switches::kEnableTouchDragDrop);
923  scoped_ptr<views::Widget> widget(CreateNewWidget());
924  DragTestView* drag_view = new DragTestView;
925  AddViewToWidgetAndResize(widget.get(), drag_view);
926  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
927                                     widget->GetNativeView());
928
929  generator.PressTouch();
930  gfx::Point point = gfx::Rect(drag_view->bounds()).CenterPoint();
931  DispatchGesture(ui::ET_GESTURE_LONG_PRESS, point);
932  DispatchGesture(ui::ET_GESTURE_LONG_TAP, point);
933
934  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
935  EXPECT_TRUE(drag_drop_controller_->drag_canceled_);
936  EXPECT_EQ(0, drag_drop_controller_->num_drag_updates_);
937  EXPECT_FALSE(drag_drop_controller_->drop_received_);
938  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
939            drag_drop_controller_->drag_string_);
940  EXPECT_EQ(0, drag_view->num_drag_enters_);
941  EXPECT_EQ(0, drag_view->num_drops_);
942  EXPECT_EQ(0, drag_view->num_drag_exits_);
943  EXPECT_TRUE(drag_view->drag_done_received_);
944}
945
946TEST_F(DragDropControllerTest, TouchDragDropLongTapGestureIsForwarded) {
947  CommandLine::ForCurrentProcess()->AppendSwitch(
948      switches::kEnableTouchDragDrop);
949  scoped_ptr<views::Widget> widget(CreateNewWidget());
950  DragTestView* drag_view = new DragTestView;
951  AddViewToWidgetAndResize(widget.get(), drag_view);
952  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
953                                     widget->GetNativeView());
954
955  generator.PressTouch();
956  gfx::Point point = gfx::Rect(drag_view->bounds()).CenterPoint();
957  DispatchGesture(ui::ET_GESTURE_LONG_PRESS, point);
958
959  // Since we are not running inside a nested loop, the |drag_source_window_|
960  // will get destroyed immediately. Hence we reassign it.
961  EXPECT_EQ(NULL, GetDragSourceWindow());
962  SetDragSourceWindow(widget->GetNativeView());
963  EXPECT_FALSE(drag_view->long_tap_received_);
964  DispatchGesture(ui::ET_GESTURE_LONG_TAP, point);
965  CompleteCancelAnimation();
966  EXPECT_TRUE(drag_view->long_tap_received_);
967}
968
969namespace {
970
971class DragImageWindowObserver : public aura::WindowObserver {
972 public:
973  virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
974    window_location_on_destroying_ = window->GetBoundsInScreen().origin();
975  }
976
977  gfx::Point window_location_on_destroying() const {
978    return window_location_on_destroying_;
979  }
980
981 public:
982  gfx::Point window_location_on_destroying_;
983};
984
985}
986
987// Verifies the drag image moves back to the position where drag is started
988// across displays when drag is cancelled.
989TEST_F(DragDropControllerTest, DragCancelAcrossDisplays) {
990  if (!SupportsMultipleDisplays())
991    return;
992
993  UpdateDisplay("400x400,400x400");
994  aura::Window::Windows root_windows =
995      Shell::GetInstance()->GetAllRootWindows();
996  for (aura::Window::Windows::iterator iter = root_windows.begin();
997       iter != root_windows.end(); ++iter) {
998    aura::client::SetDragDropClient(*iter, drag_drop_controller_.get());
999  }
1000
1001  ui::OSExchangeData data;
1002  data.SetString(base::UTF8ToUTF16("I am being dragged"));
1003  {
1004    scoped_ptr<views::Widget> widget(CreateNewWidget());
1005    aura::Window* window = widget->GetNativeWindow();
1006    drag_drop_controller_->StartDragAndDrop(
1007        data,
1008        window->GetRootWindow(),
1009        window,
1010        gfx::Point(5, 5),
1011        ui::DragDropTypes::DRAG_MOVE,
1012        ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
1013
1014    DragImageWindowObserver observer;
1015    ASSERT_TRUE(GetDragImageWindow());
1016    GetDragImageWindow()->AddObserver(&observer);
1017
1018    {
1019      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED,
1020                       gfx::Point(200, 0),
1021                       gfx::Point(200, 0),
1022                       ui::EF_NONE,
1023                       ui::EF_NONE);
1024      drag_drop_controller_->DragUpdate(window, e);
1025    }
1026    {
1027      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED,
1028                       gfx::Point(600, 0),
1029                       gfx::Point(600, 0),
1030                       ui::EF_NONE,
1031                       ui::EF_NONE);
1032      drag_drop_controller_->DragUpdate(window, e);
1033    }
1034
1035    drag_drop_controller_->DragCancel();
1036    CompleteCancelAnimation();
1037
1038    EXPECT_EQ("5,5", observer.window_location_on_destroying().ToString());
1039  }
1040
1041  {
1042    scoped_ptr<views::Widget> widget(CreateNewWidget());
1043    aura::Window* window = widget->GetNativeWindow();
1044    drag_drop_controller_->StartDragAndDrop(
1045        data,
1046        window->GetRootWindow(),
1047        window,
1048        gfx::Point(405, 405),
1049        ui::DragDropTypes::DRAG_MOVE,
1050        ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
1051    DragImageWindowObserver observer;
1052    ASSERT_TRUE(GetDragImageWindow());
1053    GetDragImageWindow()->AddObserver(&observer);
1054
1055    {
1056      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED,
1057                       gfx::Point(600, 0),
1058                       gfx::Point(600, 0),
1059                       ui::EF_NONE,
1060                       ui::EF_NONE);
1061      drag_drop_controller_->DragUpdate(window, e);
1062    }
1063    {
1064      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED,
1065                       gfx::Point(200, 0),
1066                       gfx::Point(200, 0),
1067                       ui::EF_NONE,
1068                       ui::EF_NONE);
1069      drag_drop_controller_->DragUpdate(window, e);
1070    }
1071
1072    drag_drop_controller_->DragCancel();
1073    CompleteCancelAnimation();
1074
1075    EXPECT_EQ("405,405", observer.window_location_on_destroying().ToString());
1076  }
1077  for (aura::Window::Windows::iterator iter = root_windows.begin();
1078       iter != root_windows.end(); ++iter) {
1079    aura::client::SetDragDropClient(*iter, NULL);
1080  }
1081}
1082
1083}  // namespace test
1084}  // namespace aura
1085