root/ash/drag_drop/drag_drop_controller_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Reset
  2. VerticalDragThreshold
  3. HorizontalDragThreshold
  4. GetDragOperations
  5. WriteDragData
  6. OnMousePressed
  7. OnGestureEvent
  8. GetDropFormats
  9. CanDrop
  10. OnDragEntered
  11. OnDragUpdated
  12. OnDragExited
  13. OnPerformDrop
  14. OnDragDone
  15. duration_
  16. Complete
  17. Reset
  18. StartDragAndDrop
  19. DragUpdate
  20. Drop
  21. DragCancel
  22. CreateCancelAnimation
  23. DoDragCancel
  24. check_if_capture_lost_
  25. set_check_if_capture_lost
  26. OnCaptureLost
  27. SetCheckIfCaptureLost
  28. CreateNewWidget
  29. AddViewToWidgetAndResize
  30. DispatchGesture
  31. SetUp
  32. TearDown
  33. UpdateDragData
  34. GetDragWindow
  35. GetDragSourceWindow
  36. SetDragSourceWindow
  37. GetDragImageWindow
  38. drag_drop_tracker
  39. CompleteCancelAnimation
  40. TEST_F
  41. TEST_F
  42. TEST_F
  43. TEST_F
  44. TEST_F
  45. TEST_F
  46. TEST_F
  47. TEST_F
  48. TEST_F
  49. TEST_F
  50. TEST_F
  51. TEST_F
  52. TEST_F
  53. OnWindowDestroying
  54. window_location_on_destroying
  55. TEST_F

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/drag_drop/drag_drop_controller.h"

#include "ash/drag_drop/drag_drop_tracker.h"
#include "ash/drag_drop/drag_image_view.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/aura/client/capture_client.h"
#include "ui/aura/test/event_generator.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/drag_utils.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/ui_base_switches.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
#include "ui/events/gestures/gesture_types.h"
#include "ui/gfx/animation/linear_animation.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "ui/views/test/test_views_delegate.h"
#include "ui/views/view.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/native_widget_aura.h"
#include "ui/views/widget/native_widget_delegate.h"
#include "ui/views/widget/widget.h"

namespace ash {
namespace test {

namespace {

// A simple view that makes sure RunShellDrag is invoked on mouse drag.
class DragTestView : public views::View {
 public:
  DragTestView() : views::View() {
    Reset();
  }

  void Reset() {
    num_drag_enters_ = 0;
    num_drag_exits_ = 0;
    num_drag_updates_ = 0;
    num_drops_ = 0;
    drag_done_received_ = false;
    long_tap_received_ = false;
  }

  int VerticalDragThreshold() {
    return views::View::GetVerticalDragThreshold();
  }

  int HorizontalDragThreshold() {
    return views::View::GetHorizontalDragThreshold();
  }

  int num_drag_enters_;
  int num_drag_exits_;
  int num_drag_updates_;
  int num_drops_;
  bool drag_done_received_;
  bool long_tap_received_;

 private:
  // View overrides:
  virtual int GetDragOperations(const gfx::Point& press_pt) OVERRIDE {
    return ui::DragDropTypes::DRAG_COPY;
  }

  virtual void WriteDragData(const gfx::Point& p,
                             OSExchangeData* data) OVERRIDE {
    data->SetString(base::UTF8ToUTF16("I am being dragged"));
    gfx::ImageSkiaRep image_rep(gfx::Size(10, 20), 1.0f);
    gfx::ImageSkia image_skia(image_rep);

    drag_utils::SetDragImageOnDataObject(
        image_skia, image_skia.size(), gfx::Vector2d(), data);
  }

  virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
    return true;
  }

  virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
    if (event->type() == ui::ET_GESTURE_LONG_TAP)
      long_tap_received_ = true;
    return;
  }

  virtual bool GetDropFormats(
      int* formats,
      std::set<OSExchangeData::CustomFormat>* custom_formats) OVERRIDE {
    *formats = ui::OSExchangeData::STRING;
    return true;
  }

  virtual bool CanDrop(const OSExchangeData& data) OVERRIDE {
    return true;
  }

  virtual void OnDragEntered(const ui::DropTargetEvent& event) OVERRIDE {
    num_drag_enters_++;
  }

  virtual int OnDragUpdated(const ui::DropTargetEvent& event) OVERRIDE {
    num_drag_updates_++;
    return ui::DragDropTypes::DRAG_COPY;
  }

  virtual void OnDragExited() OVERRIDE {
    num_drag_exits_++;
  }

  virtual int OnPerformDrop(const ui::DropTargetEvent& event) OVERRIDE {
    num_drops_++;
    return ui::DragDropTypes::DRAG_COPY;
  }

  virtual void OnDragDone() OVERRIDE {
    drag_done_received_ = true;
  }

  DISALLOW_COPY_AND_ASSIGN(DragTestView);
};

class CompletableLinearAnimation : public gfx::LinearAnimation {
 public:
  CompletableLinearAnimation(int duration,
                             int frame_rate,
                             gfx::AnimationDelegate* delegate)
      : gfx::LinearAnimation(duration, frame_rate, delegate),
        duration_(duration) {
  }

  void Complete() {
    Step(start_time() + base::TimeDelta::FromMilliseconds(duration_));
  }

 private:
  int duration_;
};

class TestDragDropController : public DragDropController {
 public:
  TestDragDropController() : DragDropController() { Reset(); }

  void Reset() {
    drag_start_received_ = false;
    num_drag_updates_ = 0;
    drop_received_ = false;
    drag_canceled_ = false;
    drag_string_.clear();
  }

  virtual int StartDragAndDrop(
      const ui::OSExchangeData& data,
      aura::Window* root_window,
      aura::Window* source_window,
      const gfx::Point& location,
      int operation,
      ui::DragDropTypes::DragEventSource source) OVERRIDE {
    drag_start_received_ = true;
    data.GetString(&drag_string_);
    return DragDropController::StartDragAndDrop(
        data, root_window, source_window, location, operation, source);
  }

  virtual void DragUpdate(aura::Window* target,
                          const ui::LocatedEvent& event) OVERRIDE {
    DragDropController::DragUpdate(target, event);
    num_drag_updates_++;
  }

  virtual void Drop(aura::Window* target,
                    const ui::LocatedEvent& event) OVERRIDE {
    DragDropController::Drop(target, event);
    drop_received_ = true;
  }

  virtual void DragCancel() OVERRIDE {
    DragDropController::DragCancel();
    drag_canceled_ = true;
  }

  virtual gfx::LinearAnimation* CreateCancelAnimation(
      int duration,
      int frame_rate,
      gfx::AnimationDelegate* delegate) OVERRIDE {
    return new CompletableLinearAnimation(duration, frame_rate, delegate);
  }

  virtual void DoDragCancel(int animation_duration_ms) OVERRIDE {
    DragDropController::DoDragCancel(animation_duration_ms);
    drag_canceled_ = true;
  }

  bool drag_start_received_;
  int num_drag_updates_;
  bool drop_received_;
  bool drag_canceled_;
  base::string16 drag_string_;

 private:
  DISALLOW_COPY_AND_ASSIGN(TestDragDropController);
};

class TestNativeWidgetAura : public views::NativeWidgetAura {
 public:
  explicit TestNativeWidgetAura(views::internal::NativeWidgetDelegate* delegate)
      : NativeWidgetAura(delegate),
        check_if_capture_lost_(false) {
  }

  void set_check_if_capture_lost(bool value) {
    check_if_capture_lost_ = value;
  }

  virtual void OnCaptureLost() OVERRIDE {
    DCHECK(!check_if_capture_lost_);
    views::NativeWidgetAura::OnCaptureLost();
  }

 private:
  bool check_if_capture_lost_;

  DISALLOW_COPY_AND_ASSIGN(TestNativeWidgetAura);
};

// TODO(sky): this is for debugging, remove when track down failure.
void SetCheckIfCaptureLost(views::Widget* widget, bool value) {
  // On Windows, the DCHECK triggers when running on bot or locally through RDP,
  // but not when logged in locally.
#if !defined(OS_WIN)
  static_cast<TestNativeWidgetAura*>(widget->native_widget())->
      set_check_if_capture_lost(value);
#endif
}

views::Widget* CreateNewWidget() {
  views::Widget* widget = new views::Widget;
  views::Widget::InitParams params;
  params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
  params.accept_events = true;
  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  params.parent = Shell::GetPrimaryRootWindow();
  params.child = true;
  params.native_widget = new TestNativeWidgetAura(widget);
  widget->Init(params);
  widget->Show();
  return widget;
}

void AddViewToWidgetAndResize(views::Widget* widget, views::View* view) {
  if (!widget->GetContentsView()) {
    views::View* contents_view = new views::View;
    widget->SetContentsView(contents_view);
  }

  views::View* contents_view = widget->GetContentsView();
  contents_view->AddChildView(view);
  view->SetBounds(contents_view->width(), 0, 100, 100);
  gfx::Rect contents_view_bounds = contents_view->bounds();
  contents_view_bounds.Union(view->bounds());
  contents_view->SetBoundsRect(contents_view_bounds);
  widget->SetBounds(contents_view_bounds);
}

void DispatchGesture(ui::EventType gesture_type, gfx::Point location) {
  ui::GestureEvent gesture_event(
      gesture_type,
      location.x(),
      location.y(),
      0,
      ui::EventTimeForNow(),
      ui::GestureEventDetails(gesture_type, 0, 0),
      1);
  Shell::GetPrimaryRootWindow()->GetHost()->dispatcher()->DispatchGestureEvent(
      &gesture_event);
}

}  // namespace

class DragDropControllerTest : public AshTestBase {
 public:
  DragDropControllerTest() : AshTestBase() {}
  virtual ~DragDropControllerTest() {}

  virtual void SetUp() OVERRIDE {
    AshTestBase::SetUp();
    drag_drop_controller_.reset(new TestDragDropController);
    drag_drop_controller_->set_should_block_during_drag_drop(false);
    aura::client::SetDragDropClient(Shell::GetPrimaryRootWindow(),
                                    drag_drop_controller_.get());
    views_delegate_.reset(new views::TestViewsDelegate);
  }

  virtual void TearDown() OVERRIDE {
    aura::client::SetDragDropClient(Shell::GetPrimaryRootWindow(), NULL);
    drag_drop_controller_.reset();
    AshTestBase::TearDown();
  }

  void UpdateDragData(ui::OSExchangeData* data) {
    drag_drop_controller_->drag_data_ = data;
  }

  aura::Window* GetDragWindow() {
    return drag_drop_controller_->drag_window_;
  }

  aura::Window* GetDragSourceWindow() {
    return drag_drop_controller_->drag_source_window_;
  }

  void SetDragSourceWindow(aura::Window* drag_source_window) {
    drag_drop_controller_->drag_source_window_ = drag_source_window;
    drag_source_window->AddObserver(drag_drop_controller_.get());
  }

  aura::Window* GetDragImageWindow() {
    return drag_drop_controller_->drag_image_.get() ?
        drag_drop_controller_->drag_image_->GetWidget()->GetNativeWindow() :
        NULL;
  }

  DragDropTracker* drag_drop_tracker() {
    return drag_drop_controller_->drag_drop_tracker_.get();
  }

  void CompleteCancelAnimation() {
    CompletableLinearAnimation* animation =
        static_cast<CompletableLinearAnimation*>(
            drag_drop_controller_->cancel_animation_.get());
    animation->Complete();
  }

 protected:
  scoped_ptr<TestDragDropController> drag_drop_controller_;
  scoped_ptr<views::TestViewsDelegate> views_delegate_;

 private:
  DISALLOW_COPY_AND_ASSIGN(DragDropControllerTest);
};

// TODO(win_aura) http://crbug.com/154081
#if defined(OS_WIN)
#define MAYBE_DragDropInSingleViewTest DISABLED_DragDropInSingleViewTest
#else
#define MAYBE_DragDropInSingleViewTest DragDropInSingleViewTest
#endif
TEST_F(DragDropControllerTest, MAYBE_DragDropInSingleViewTest) {
  scoped_ptr<views::Widget> widget(CreateNewWidget());
  DragTestView* drag_view = new DragTestView;
  AddViewToWidgetAndResize(widget.get(), drag_view);
  ui::OSExchangeData data;
  data.SetString(base::UTF8ToUTF16("I am being dragged"));
  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
                                       widget->GetNativeView());
  generator.PressLeftButton();

  int num_drags = 17;
  SetCheckIfCaptureLost(widget.get(), true);
  for (int i = 0; i < num_drags; ++i) {
    // Because we are not doing a blocking drag and drop, the original
    // OSDragExchangeData object is lost as soon as we return from the drag
    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
    // drag_data_ to a fake drag data object that we created.
    if (i > 0)
      UpdateDragData(&data);
    // 7 comes from views::View::GetVerticalDragThreshold()).
    if (i >= 7)
      SetCheckIfCaptureLost(widget.get(), false);

    generator.MoveMouseBy(0, 1);

    // Execute any scheduled draws to process deferred mouse events.
    RunAllPendingInMessageLoop();
  }

  generator.ReleaseLeftButton();

  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
      drag_drop_controller_->num_drag_updates_);
  EXPECT_TRUE(drag_drop_controller_->drop_received_);
  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
      drag_drop_controller_->drag_string_);

  EXPECT_EQ(1, drag_view->num_drag_enters_);
  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
      drag_view->num_drag_updates_);
  EXPECT_EQ(1, drag_view->num_drops_);
  EXPECT_EQ(0, drag_view->num_drag_exits_);
  EXPECT_TRUE(drag_view->drag_done_received_);
}

TEST_F(DragDropControllerTest, DragDropWithZeroDragUpdates) {
  scoped_ptr<views::Widget> widget(CreateNewWidget());
  DragTestView* drag_view = new DragTestView;
  AddViewToWidgetAndResize(widget.get(), drag_view);
  ui::OSExchangeData data;
  data.SetString(base::UTF8ToUTF16("I am being dragged"));
  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
                                       widget->GetNativeView());
  generator.PressLeftButton();

  int num_drags = drag_view->VerticalDragThreshold() + 1;
  for (int i = 0; i < num_drags; ++i) {
    // Because we are not doing a blocking drag and drop, the original
    // OSDragExchangeData object is lost as soon as we return from the drag
    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
    // drag_data_ to a fake drag data object that we created.
    if (i > 0)
      UpdateDragData(&data);
    generator.MoveMouseBy(0, 1);
  }

  UpdateDragData(&data);

  generator.ReleaseLeftButton();

  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold() + 1,
      drag_drop_controller_->num_drag_updates_);
  EXPECT_TRUE(drag_drop_controller_->drop_received_);

  EXPECT_EQ(1, drag_view->num_drag_enters_);
  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold() + 1,
      drag_view->num_drag_updates_);
  EXPECT_EQ(1, drag_view->num_drops_);
  EXPECT_EQ(0, drag_view->num_drag_exits_);
  EXPECT_TRUE(drag_view->drag_done_received_);
}

// TODO(win_aura) http://crbug.com/154081
#if defined(OS_WIN)
#define MAYBE_DragDropInMultipleViewsSingleWidgetTest DISABLED_DragDropInMultipleViewsSingleWidgetTest
#else
#define MAYBE_DragDropInMultipleViewsSingleWidgetTest DragDropInMultipleViewsSingleWidgetTest
#endif
TEST_F(DragDropControllerTest, MAYBE_DragDropInMultipleViewsSingleWidgetTest) {
  scoped_ptr<views::Widget> widget(CreateNewWidget());
  DragTestView* drag_view1 = new DragTestView;
  AddViewToWidgetAndResize(widget.get(), drag_view1);
  DragTestView* drag_view2 = new DragTestView;
  AddViewToWidgetAndResize(widget.get(), drag_view2);

  ui::OSExchangeData data;
  data.SetString(base::UTF8ToUTF16("I am being dragged"));

  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
  generator.MoveMouseRelativeTo(widget->GetNativeView(),
                                drag_view1->bounds().CenterPoint());
  generator.PressLeftButton();

  int num_drags = drag_view1->width();
  for (int i = 0; i < num_drags; ++i) {
    // Because we are not doing a blocking drag and drop, the original
    // OSDragExchangeData object is lost as soon as we return from the drag
    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
    // drag_data_ to a fake drag data object that we created.
    if (i > 0)
      UpdateDragData(&data);
    generator.MoveMouseBy(1, 0);

    // Execute any scheduled draws to process deferred mouse events.
    RunAllPendingInMessageLoop();
  }

  generator.ReleaseLeftButton();

  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
  EXPECT_EQ(num_drags - 1 - drag_view1->HorizontalDragThreshold(),
      drag_drop_controller_->num_drag_updates_);
  EXPECT_TRUE(drag_drop_controller_->drop_received_);
  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
      drag_drop_controller_->drag_string_);

  EXPECT_EQ(1, drag_view1->num_drag_enters_);
  int num_expected_updates = drag_view1->bounds().width() -
      drag_view1->bounds().CenterPoint().x() - 2;
  EXPECT_EQ(num_expected_updates - drag_view1->HorizontalDragThreshold(),
      drag_view1->num_drag_updates_);
  EXPECT_EQ(0, drag_view1->num_drops_);
  EXPECT_EQ(1, drag_view1->num_drag_exits_);
  EXPECT_TRUE(drag_view1->drag_done_received_);

  EXPECT_EQ(1, drag_view2->num_drag_enters_);
  num_expected_updates = num_drags - num_expected_updates - 1;
  EXPECT_EQ(num_expected_updates, drag_view2->num_drag_updates_);
  EXPECT_EQ(1, drag_view2->num_drops_);
  EXPECT_EQ(0, drag_view2->num_drag_exits_);
  EXPECT_FALSE(drag_view2->drag_done_received_);
}

// TODO(win_aura) http://crbug.com/154081
#if defined(OS_WIN)
#define MAYBE_DragDropInMultipleViewsMultipleWidgetsTest DISABLED_DragDropInMultipleViewsMultipleWidgetsTest
#else
#define MAYBE_DragDropInMultipleViewsMultipleWidgetsTest DragDropInMultipleViewsMultipleWidgetsTest
#endif
TEST_F(DragDropControllerTest, MAYBE_DragDropInMultipleViewsMultipleWidgetsTest) {
  scoped_ptr<views::Widget> widget1(CreateNewWidget());
  DragTestView* drag_view1 = new DragTestView;
  AddViewToWidgetAndResize(widget1.get(), drag_view1);
  scoped_ptr<views::Widget> widget2(CreateNewWidget());
  DragTestView* drag_view2 = new DragTestView;
  AddViewToWidgetAndResize(widget2.get(), drag_view2);
  gfx::Rect widget1_bounds = widget1->GetClientAreaBoundsInScreen();
  gfx::Rect widget2_bounds = widget2->GetClientAreaBoundsInScreen();
  widget2->SetBounds(gfx::Rect(widget1_bounds.width(), 0,
      widget2_bounds.width(), widget2_bounds.height()));

  ui::OSExchangeData data;
  data.SetString(base::UTF8ToUTF16("I am being dragged"));

  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
                                       widget1->GetNativeView());
  generator.PressLeftButton();

  int num_drags = drag_view1->width();
  for (int i = 0; i < num_drags; ++i) {
    // Because we are not doing a blocking drag and drop, the original
    // OSDragExchangeData object is lost as soon as we return from the drag
    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
    // drag_data_ to a fake drag data object that we created.
    if (i > 0)
      UpdateDragData(&data);
    generator.MoveMouseBy(1, 0);

    // Execute any scheduled draws to process deferred mouse events.
    RunAllPendingInMessageLoop();
  }

  generator.ReleaseLeftButton();

  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
  EXPECT_EQ(num_drags - 1 - drag_view1->HorizontalDragThreshold(),
      drag_drop_controller_->num_drag_updates_);
  EXPECT_TRUE(drag_drop_controller_->drop_received_);
  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
      drag_drop_controller_->drag_string_);

  EXPECT_EQ(1, drag_view1->num_drag_enters_);
  int num_expected_updates = drag_view1->bounds().width() -
      drag_view1->bounds().CenterPoint().x() - 2;
  EXPECT_EQ(num_expected_updates - drag_view1->HorizontalDragThreshold(),
      drag_view1->num_drag_updates_);
  EXPECT_EQ(0, drag_view1->num_drops_);
  EXPECT_EQ(1, drag_view1->num_drag_exits_);
  EXPECT_TRUE(drag_view1->drag_done_received_);

  EXPECT_EQ(1, drag_view2->num_drag_enters_);
  num_expected_updates = num_drags - num_expected_updates - 1;
  EXPECT_EQ(num_expected_updates, drag_view2->num_drag_updates_);
  EXPECT_EQ(1, drag_view2->num_drops_);
  EXPECT_EQ(0, drag_view2->num_drag_exits_);
  EXPECT_FALSE(drag_view2->drag_done_received_);
}

// TODO(win_aura) http://crbug.com/154081
#if defined(OS_WIN)
#define MAYBE_ViewRemovedWhileInDragDropTest DISABLED_ViewRemovedWhileInDragDropTest
#else
#define MAYBE_ViewRemovedWhileInDragDropTest ViewRemovedWhileInDragDropTest
#endif
TEST_F(DragDropControllerTest, MAYBE_ViewRemovedWhileInDragDropTest) {
  scoped_ptr<views::Widget> widget(CreateNewWidget());
  scoped_ptr<DragTestView> drag_view(new DragTestView);
  AddViewToWidgetAndResize(widget.get(), drag_view.get());
  gfx::Point point = gfx::Rect(drag_view->bounds()).CenterPoint();
  ui::OSExchangeData data;
  data.SetString(base::UTF8ToUTF16("I am being dragged"));

  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
  generator.MoveMouseToCenterOf(widget->GetNativeView());
  generator.PressLeftButton();

  int num_drags_1 = 17;
  for (int i = 0; i < num_drags_1; ++i) {
    // Because we are not doing a blocking drag and drop, the original
    // OSDragExchangeData object is lost as soon as we return from the drag
    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
    // drag_data_ to a fake drag data object that we created.
    if (i > 0)
      UpdateDragData(&data);
    generator.MoveMouseBy(0, 1);

    // Execute any scheduled draws to process deferred mouse events.
    RunAllPendingInMessageLoop();
  }

  drag_view->parent()->RemoveChildView(drag_view.get());
  // View has been removed. We will not get any of the following drag updates.
  int num_drags_2 = 23;
  for (int i = 0; i < num_drags_2; ++i) {
    UpdateDragData(&data);
    generator.MoveMouseBy(0, 1);

    // Execute any scheduled draws to process deferred mouse events.
    RunAllPendingInMessageLoop();
  }

  generator.ReleaseLeftButton();

  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
  EXPECT_EQ(num_drags_1 + num_drags_2 - 1 - drag_view->VerticalDragThreshold(),
      drag_drop_controller_->num_drag_updates_);
  EXPECT_TRUE(drag_drop_controller_->drop_received_);
  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
      drag_drop_controller_->drag_string_);

  EXPECT_EQ(1, drag_view->num_drag_enters_);
  EXPECT_EQ(num_drags_1 - 1 - drag_view->VerticalDragThreshold(),
      drag_view->num_drag_updates_);
  EXPECT_EQ(0, drag_view->num_drops_);
  EXPECT_EQ(0, drag_view->num_drag_exits_);
  EXPECT_TRUE(drag_view->drag_done_received_);
}

TEST_F(DragDropControllerTest, DragLeavesClipboardAloneTest) {
  ui::Clipboard* cb = ui::Clipboard::GetForCurrentThread();
  std::string clip_str("I am on the clipboard");
  {
    // We first copy some text to the clipboard.
    ui::ScopedClipboardWriter scw(cb, ui::CLIPBOARD_TYPE_COPY_PASTE);
    scw.WriteText(base::ASCIIToUTF16(clip_str));
  }
  EXPECT_TRUE(cb->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(),
      ui::CLIPBOARD_TYPE_COPY_PASTE));

  scoped_ptr<views::Widget> widget(CreateNewWidget());
  DragTestView* drag_view = new DragTestView;
  AddViewToWidgetAndResize(widget.get(), drag_view);

  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
                                       widget->GetNativeView());
  ui::OSExchangeData data;
  std::string data_str("I am being dragged");
  data.SetString(base::ASCIIToUTF16(data_str));

  generator.PressLeftButton();
  generator.MoveMouseBy(0, drag_view->VerticalDragThreshold() + 1);

  // Execute any scheduled draws to process deferred mouse events.
  RunAllPendingInMessageLoop();

  // Verify the clipboard contents haven't changed
  std::string result;
  EXPECT_TRUE(cb->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(),
      ui::CLIPBOARD_TYPE_COPY_PASTE));
  cb->ReadAsciiText(ui::CLIPBOARD_TYPE_COPY_PASTE, &result);
  EXPECT_EQ(clip_str, result);
  // Destory the clipboard here because ash doesn't delete it.
  // crbug.com/158150.
  ui::Clipboard::DestroyClipboardForCurrentThread();
}

TEST_F(DragDropControllerTest, WindowDestroyedDuringDragDrop) {
  scoped_ptr<views::Widget> widget(CreateNewWidget());
  DragTestView* drag_view = new DragTestView;
  AddViewToWidgetAndResize(widget.get(), drag_view);
  aura::Window* window = widget->GetNativeView();

  ui::OSExchangeData data;
  data.SetString(base::UTF8ToUTF16("I am being dragged"));
  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
                                       widget->GetNativeView());
  generator.PressLeftButton();

  int num_drags = 17;
  for (int i = 0; i < num_drags; ++i) {
    // Because we are not doing a blocking drag and drop, the original
    // OSDragExchangeData object is lost as soon as we return from the drag
    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
    // drag_data_ to a fake drag data object that we created.
    if (i > 0)
      UpdateDragData(&data);
    generator.MoveMouseBy(0, 1);

    // Execute any scheduled draws to process deferred mouse events.
    RunAllPendingInMessageLoop();

    if (i > drag_view->VerticalDragThreshold())
      EXPECT_EQ(window, GetDragWindow());
  }

  widget->CloseNow();
  EXPECT_FALSE(GetDragWindow());

  num_drags = 23;
  for (int i = 0; i < num_drags; ++i) {
    if (i > 0)
      UpdateDragData(&data);
    generator.MoveMouseBy(0, 1);
    // We should not crash here.
  }

  generator.ReleaseLeftButton();

  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
  EXPECT_TRUE(drag_drop_controller_->drop_received_);
}

TEST_F(DragDropControllerTest, SyntheticEventsDuringDragDrop) {
  scoped_ptr<views::Widget> widget(CreateNewWidget());
  DragTestView* drag_view = new DragTestView;
  AddViewToWidgetAndResize(widget.get(), drag_view);
  ui::OSExchangeData data;
  data.SetString(base::UTF8ToUTF16("I am being dragged"));
  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
                                       widget->GetNativeView());
  generator.PressLeftButton();

  int num_drags = 17;
  for (int i = 0; i < num_drags; ++i) {
    // Because we are not doing a blocking drag and drop, the original
    // OSDragExchangeData object is lost as soon as we return from the drag
    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
    // drag_data_ to a fake drag data object that we created.
    if (i > 0)
      UpdateDragData(&data);
    generator.MoveMouseBy(0, 1);

    // We send a unexpected mouse move event. Note that we cannot use
    // EventGenerator since it implicitly turns these into mouse drag events.
    // The DragDropController should simply ignore these events.
    gfx::Point mouse_move_location = drag_view->bounds().CenterPoint();
    ui::MouseEvent mouse_move(ui::ET_MOUSE_MOVED, mouse_move_location,
                              mouse_move_location, 0, 0);
    ui::EventDispatchDetails details = Shell::GetPrimaryRootWindow()->
        GetHost()->event_processor()->OnEventFromSource(&mouse_move);
    ASSERT_FALSE(details.dispatcher_destroyed);
  }

  generator.ReleaseLeftButton();

  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
      drag_drop_controller_->num_drag_updates_);
  EXPECT_TRUE(drag_drop_controller_->drop_received_);
  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
      drag_drop_controller_->drag_string_);

  EXPECT_EQ(1, drag_view->num_drag_enters_);
  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
      drag_view->num_drag_updates_);
  EXPECT_EQ(1, drag_view->num_drops_);
  EXPECT_EQ(0, drag_view->num_drag_exits_);
  EXPECT_TRUE(drag_view->drag_done_received_);
}

// TODO(win_aura) http://crbug.com/154081
#if defined(OS_WIN)
#define MAYBE_PressingEscapeCancelsDragDrop DISABLED_PressingEscapeCancelsDragDrop
#define MAYBE_CaptureLostCancelsDragDrop DISABLED_CaptureLostCancelsDragDrop
#else
#define MAYBE_PressingEscapeCancelsDragDrop PressingEscapeCancelsDragDrop
#define MAYBE_CaptureLostCancelsDragDrop CaptureLostCancelsDragDrop
#endif
TEST_F(DragDropControllerTest, MAYBE_PressingEscapeCancelsDragDrop) {
  scoped_ptr<views::Widget> widget(CreateNewWidget());
  DragTestView* drag_view = new DragTestView;
  AddViewToWidgetAndResize(widget.get(), drag_view);
  ui::OSExchangeData data;
  data.SetString(base::UTF8ToUTF16("I am being dragged"));
  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
                                       widget->GetNativeView());
  generator.PressLeftButton();

  int num_drags = 17;
  for (int i = 0; i < num_drags; ++i) {
    // Because we are not doing a blocking drag and drop, the original
    // OSDragExchangeData object is lost as soon as we return from the drag
    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
    // drag_data_ to a fake drag data object that we created.
    if (i > 0)
      UpdateDragData(&data);
    generator.MoveMouseBy(0, 1);

    // Execute any scheduled draws to process deferred mouse events.
    RunAllPendingInMessageLoop();
  }

  generator.PressKey(ui::VKEY_ESCAPE, 0);

  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
      drag_drop_controller_->num_drag_updates_);
  EXPECT_FALSE(drag_drop_controller_->drop_received_);
  EXPECT_TRUE(drag_drop_controller_->drag_canceled_);
  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
      drag_drop_controller_->drag_string_);

  EXPECT_EQ(1, drag_view->num_drag_enters_);
  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
      drag_view->num_drag_updates_);
  EXPECT_EQ(0, drag_view->num_drops_);
  EXPECT_EQ(1, drag_view->num_drag_exits_);
  EXPECT_TRUE(drag_view->drag_done_received_);
}

TEST_F(DragDropControllerTest, MAYBE_CaptureLostCancelsDragDrop) {
  scoped_ptr<views::Widget> widget(CreateNewWidget());
  DragTestView* drag_view = new DragTestView;
  AddViewToWidgetAndResize(widget.get(), drag_view);
  ui::OSExchangeData data;
  data.SetString(base::UTF8ToUTF16("I am being dragged"));
  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
                                       widget->GetNativeView());
  generator.PressLeftButton();

  int num_drags = 17;
  for (int i = 0; i < num_drags; ++i) {
    // Because we are not doing a blocking drag and drop, the original
    // OSDragExchangeData object is lost as soon as we return from the drag
    // initiation in DragDropController::StartDragAndDrop(). Hence we set the
    // drag_data_ to a fake drag data object that we created.
    if (i > 0)
      UpdateDragData(&data);
    generator.MoveMouseBy(0, 1);

    // Execute any scheduled draws to process deferred mouse events.
    RunAllPendingInMessageLoop();
  }
  // Make sure the capture window won't handle mouse events.
  aura::Window* capture_window = drag_drop_tracker()->capture_window();
  ASSERT_TRUE(!!capture_window);
  EXPECT_EQ("0x0", capture_window->bounds().size().ToString());
  EXPECT_EQ(NULL,
            capture_window->GetEventHandlerForPoint(gfx::Point()));
  EXPECT_EQ(NULL,
            capture_window->GetTopWindowContainingPoint(gfx::Point()));

  aura::client::GetCaptureClient(widget->GetNativeView()->GetRootWindow())->
      SetCapture(NULL);

  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
      drag_drop_controller_->num_drag_updates_);
  EXPECT_FALSE(drag_drop_controller_->drop_received_);
  EXPECT_TRUE(drag_drop_controller_->drag_canceled_);
  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
      drag_drop_controller_->drag_string_);

  EXPECT_EQ(1, drag_view->num_drag_enters_);
  EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(),
      drag_view->num_drag_updates_);
  EXPECT_EQ(0, drag_view->num_drops_);
  EXPECT_EQ(1, drag_view->num_drag_exits_);
  EXPECT_TRUE(drag_view->drag_done_received_);
}

TEST_F(DragDropControllerTest, TouchDragDropInMultipleWindows) {
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableTouchDragDrop);
  scoped_ptr<views::Widget> widget1(CreateNewWidget());
  DragTestView* drag_view1 = new DragTestView;
  AddViewToWidgetAndResize(widget1.get(), drag_view1);
  scoped_ptr<views::Widget> widget2(CreateNewWidget());
  DragTestView* drag_view2 = new DragTestView;
  AddViewToWidgetAndResize(widget2.get(), drag_view2);
  gfx::Rect widget1_bounds = widget1->GetClientAreaBoundsInScreen();
  gfx::Rect widget2_bounds = widget2->GetClientAreaBoundsInScreen();
  widget2->SetBounds(gfx::Rect(widget1_bounds.width(), 0,
      widget2_bounds.width(), widget2_bounds.height()));

  ui::OSExchangeData data;
  data.SetString(base::UTF8ToUTF16("I am being dragged"));

  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
                                       widget1->GetNativeView());
  generator.PressTouch();
  gfx::Point point = gfx::Rect(drag_view1->bounds()).CenterPoint();
  DispatchGesture(ui::ET_GESTURE_LONG_PRESS, point);
  // Because we are not doing a blocking drag and drop, the original
  // OSDragExchangeData object is lost as soon as we return from the drag
  // initiation in DragDropController::StartDragAndDrop(). Hence we set the
  // drag_data_ to a fake drag data object that we created.
  UpdateDragData(&data);
  gfx::Point gesture_location = point;
  int num_drags = drag_view1->width();
  for (int i = 0; i < num_drags; ++i) {
    gesture_location.Offset(1, 0);
    DispatchGesture(ui::ET_GESTURE_SCROLL_UPDATE, gesture_location);

    // Execute any scheduled draws to process deferred mouse events.
    RunAllPendingInMessageLoop();
  }

  DispatchGesture(ui::ET_GESTURE_SCROLL_END, gesture_location);

  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
  EXPECT_EQ(num_drags, drag_drop_controller_->num_drag_updates_);
  EXPECT_TRUE(drag_drop_controller_->drop_received_);
  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
      drag_drop_controller_->drag_string_);

  EXPECT_EQ(1, drag_view1->num_drag_enters_);
  int num_expected_updates = drag_view1->bounds().width() -
      drag_view1->bounds().CenterPoint().x() - 1;
  EXPECT_EQ(num_expected_updates, drag_view1->num_drag_updates_);
  EXPECT_EQ(0, drag_view1->num_drops_);
  EXPECT_EQ(1, drag_view1->num_drag_exits_);
  EXPECT_TRUE(drag_view1->drag_done_received_);

  EXPECT_EQ(1, drag_view2->num_drag_enters_);
  num_expected_updates = num_drags - num_expected_updates;
  EXPECT_EQ(num_expected_updates, drag_view2->num_drag_updates_);
  EXPECT_EQ(1, drag_view2->num_drops_);
  EXPECT_EQ(0, drag_view2->num_drag_exits_);
  EXPECT_FALSE(drag_view2->drag_done_received_);
}

TEST_F(DragDropControllerTest, TouchDragDropCancelsOnLongTap) {
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableTouchDragDrop);
  scoped_ptr<views::Widget> widget(CreateNewWidget());
  DragTestView* drag_view = new DragTestView;
  AddViewToWidgetAndResize(widget.get(), drag_view);
  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
                                       widget->GetNativeView());

  generator.PressTouch();
  gfx::Point point = gfx::Rect(drag_view->bounds()).CenterPoint();
  DispatchGesture(ui::ET_GESTURE_LONG_PRESS, point);
  DispatchGesture(ui::ET_GESTURE_LONG_TAP, point);

  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
  EXPECT_TRUE(drag_drop_controller_->drag_canceled_);
  EXPECT_EQ(0, drag_drop_controller_->num_drag_updates_);
  EXPECT_FALSE(drag_drop_controller_->drop_received_);
  EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"),
            drag_drop_controller_->drag_string_);
  EXPECT_EQ(0, drag_view->num_drag_enters_);
  EXPECT_EQ(0, drag_view->num_drops_);
  EXPECT_EQ(0, drag_view->num_drag_exits_);
  EXPECT_TRUE(drag_view->drag_done_received_);
}

TEST_F(DragDropControllerTest, TouchDragDropLongTapGestureIsForwarded) {
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableTouchDragDrop);
  scoped_ptr<views::Widget> widget(CreateNewWidget());
  DragTestView* drag_view = new DragTestView;
  AddViewToWidgetAndResize(widget.get(), drag_view);
  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
                                       widget->GetNativeView());

  generator.PressTouch();
  gfx::Point point = gfx::Rect(drag_view->bounds()).CenterPoint();
  DispatchGesture(ui::ET_GESTURE_LONG_PRESS, point);

  // Since we are not running inside a nested loop, the |drag_source_window_|
  // will get destroyed immediately. Hence we reassign it.
  EXPECT_EQ(NULL, GetDragSourceWindow());
  SetDragSourceWindow(widget->GetNativeView());
  EXPECT_FALSE(drag_view->long_tap_received_);
  DispatchGesture(ui::ET_GESTURE_LONG_TAP, point);
  CompleteCancelAnimation();
  EXPECT_TRUE(drag_view->long_tap_received_);
}

namespace {

class DragImageWindowObserver : public aura::WindowObserver {
 public:
  virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
    window_location_on_destroying_ = window->GetBoundsInScreen().origin();
  }

  gfx::Point window_location_on_destroying() const {
    return window_location_on_destroying_;
  }

 public:
  gfx::Point window_location_on_destroying_;
};

}

// Verifies the drag image moves back to the position where drag is started
// across displays when drag is cancelled.
TEST_F(DragDropControllerTest, DragCancelAcrossDisplays) {
  if (!SupportsMultipleDisplays())
    return;

  UpdateDisplay("400x400,400x400");
  aura::Window::Windows root_windows =
      Shell::GetInstance()->GetAllRootWindows();
  for (aura::Window::Windows::iterator iter = root_windows.begin();
       iter != root_windows.end(); ++iter) {
    aura::client::SetDragDropClient(*iter, drag_drop_controller_.get());
  }

  ui::OSExchangeData data;
  data.SetString(base::UTF8ToUTF16("I am being dragged"));
  {
    scoped_ptr<views::Widget> widget(CreateNewWidget());
    aura::Window* window = widget->GetNativeWindow();
    drag_drop_controller_->StartDragAndDrop(
        data,
        window->GetRootWindow(),
        window,
        gfx::Point(5, 5),
        ui::DragDropTypes::DRAG_MOVE,
        ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);

    DragImageWindowObserver observer;
    ASSERT_TRUE(GetDragImageWindow());
    GetDragImageWindow()->AddObserver(&observer);

    {
      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED,
                       gfx::Point(200, 0),
                       gfx::Point(200, 0),
                       ui::EF_NONE,
                       ui::EF_NONE);
      drag_drop_controller_->DragUpdate(window, e);
    }
    {
      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED,
                       gfx::Point(600, 0),
                       gfx::Point(600, 0),
                       ui::EF_NONE,
                       ui::EF_NONE);
      drag_drop_controller_->DragUpdate(window, e);
    }

    drag_drop_controller_->DragCancel();
    CompleteCancelAnimation();

    EXPECT_EQ("5,5", observer.window_location_on_destroying().ToString());
  }

  {
    scoped_ptr<views::Widget> widget(CreateNewWidget());
    aura::Window* window = widget->GetNativeWindow();
    drag_drop_controller_->StartDragAndDrop(
        data,
        window->GetRootWindow(),
        window,
        gfx::Point(405, 405),
        ui::DragDropTypes::DRAG_MOVE,
        ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
    DragImageWindowObserver observer;
    ASSERT_TRUE(GetDragImageWindow());
    GetDragImageWindow()->AddObserver(&observer);

    {
      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED,
                       gfx::Point(600, 0),
                       gfx::Point(600, 0),
                       ui::EF_NONE,
                       ui::EF_NONE);
      drag_drop_controller_->DragUpdate(window, e);
    }
    {
      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED,
                       gfx::Point(200, 0),
                       gfx::Point(200, 0),
                       ui::EF_NONE,
                       ui::EF_NONE);
      drag_drop_controller_->DragUpdate(window, e);
    }

    drag_drop_controller_->DragCancel();
    CompleteCancelAnimation();

    EXPECT_EQ("405,405", observer.window_location_on_destroying().ToString());
  }
  for (aura::Window::Windows::iterator iter = root_windows.begin();
       iter != root_windows.end(); ++iter) {
    aura::client::SetDragDropClient(*iter, NULL);
  }
}

}  // namespace test
}  // namespace aura

/* [<][>][^][v][top][bottom][index][help] */