chromium/ash/drag_drop/drag_drop_capture_delegate_unittest.cc

// Copyright 2012 The Chromium Authors
// 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_capture_delegate.h"

#include "ash/drag_drop/drag_drop_controller.h"
#include "ash/drag_drop/toplevel_window_drag_delegate.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/test_shell_delegate.h"
#include "ash/wm/window_util.h"
#include "base/functional/bind.h"
#include "base/test/bind.h"
#include "ui/aura/client/drag_drop_client.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
#include "ui/events/gesture_detection/gesture_configuration.h"
#include "ui/events/gestures/gesture_types.h"
#include "ui/events/gestures/motion_event_aura.h"

namespace ash {
namespace {

class DragDropCaptureDelegateTest : public AshTestBase {
 public:
  DragDropCaptureDelegateTest() = default;

  DragDropCaptureDelegateTest(const DragDropCaptureDelegateTest&) = delete;
  DragDropCaptureDelegateTest& operator=(const DragDropCaptureDelegateTest&) =
      delete;

  ~DragDropCaptureDelegateTest() override = default;

  // AshTestBase:
  void SetUp() override {
    drag_drop_capture_delegate_.reset(new DragDropCaptureDelegate());
    AshTestBase::SetUp(std::make_unique<TestShellDelegate>());
  }

  void TearDown() override {
    drag_drop_capture_delegate_.reset();
    AshTestBase::TearDown();
  }

 protected:
  std::unique_ptr<DragDropCaptureDelegate> drag_drop_capture_delegate_;
};

class TestWindowDelegate : public aura::test::TestWindowDelegate {
 public:
  TestWindowDelegate() = default;
  TestWindowDelegate(const TestWindowDelegate&) = delete;
  TestWindowDelegate& operator=(const TestWindowDelegate&) = delete;
  ~TestWindowDelegate() override = default;

  // ui::EventHandler:
  void OnTouchEvent(ui::TouchEvent* event) final {
    motion_event.OnTouch(*event);
    if (event->type() == ui::EventType::kTouchCancelled) {
      touch_cancel_received = true;
    }
  }

  ui::MotionEventAura motion_event;
  bool touch_cancel_received = false;
};

class TestDragDelegate : public ToplevelWindowDragDelegate {
 public:
  TestDragDelegate() = default;
  TestDragDelegate(const TestDragDelegate&) = delete;
  TestDragDelegate& operator=(const TestDragDelegate&) = delete;
  ~TestDragDelegate() override = default;

  // ToplevelWindowDragDelegate:
  void OnToplevelWindowDragStarted(const gfx::PointF& start_location,
                                   ui::mojom::DragEventSource source,
                                   aura::Window* source_window) override {}

  ::ui::mojom::DragOperation OnToplevelWindowDragDropped() override {
    return ::ui::mojom::DragOperation::kMove;
  }

  void OnToplevelWindowDragCancelled() override {}

  void OnToplevelWindowDragEvent(ui::LocatedEvent* event) override {}
};

}  // namespace

TEST_F(DragDropCaptureDelegateTest, CanTakeCaptureAndConvertToOriginalWindow) {
  TestWindowDelegate source_window_delegate;

  auto source_window = base::WrapUnique(CreateTestWindowInShellWithDelegate(
      &source_window_delegate, -1, gfx::Rect(0, 0, 100, 100)));
  source_window->Show();
  EXPECT_FALSE(source_window_delegate.touch_cancel_received);

  drag_drop_capture_delegate_->TakeCapture(
      source_window->GetRootWindow(), source_window.get(),
      base::BindLambdaForTesting([]() {}),
      ui::TransferTouchesBehavior::kCancel);

  EXPECT_TRUE(drag_drop_capture_delegate_->capture_window()->HasCapture());
  EXPECT_TRUE(source_window_delegate.touch_cancel_received);

  ui::GestureEventDetails event_details(ui::EventType::kGestureScrollUpdate);
  ui::GestureEvent gesture_event(0, 0, 0, ui::EventTimeForNow(), event_details);
  ui::Event::DispatcherApi(&gesture_event)
      .set_target(drag_drop_capture_delegate_->capture_window());
  auto* converted_target =
      drag_drop_capture_delegate_->GetTarget(gesture_event);

  EXPECT_EQ(converted_target, source_window.get());
}

// Make sure that state of the MotionEvent on source window will be set to
// cancled when the drag and drop is started with gesture.
TEST_F(DragDropCaptureDelegateTest, CanTakeCaptureAndConvertToOriginalWindow2) {
  TestWindowDelegate source_window_delegate;

  std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
      &source_window_delegate, -1, gfx::Rect(0, 0, 100, 100)));

  ui::test::EventGenerator generator(window->GetRootWindow(), window.get());

  gfx::Point point(5, 5);
  generator.PressTouch(point);

  EXPECT_EQ(source_window_delegate.motion_event.GetAction(),
            ui::MotionEvent::Action::DOWN);
  point.Offset(1, 1);
  generator.MoveTouch(point);

  EXPECT_EQ(source_window_delegate.motion_event.GetAction(),
            ui::MotionEvent::Action::MOVE);

  auto data = std::make_unique<ui::OSExchangeData>();
  auto* drag_drop_controller = static_cast<DragDropController*>(
      aura::client::GetDragDropClient(window->GetRootWindow()));

  TestDragDelegate test_drag_delegate;
  drag_drop_controller->set_toplevel_window_drag_delegate(&test_drag_delegate);
  drag_drop_controller->SetDisableNestedLoopForTesting(true);
  drag_drop_controller->StartDragAndDrop(
      std::move(data), window->GetRootWindow(), window.get(), point,
      ui::DragDropTypes::DRAG_MOVE, ui::mojom::DragEventSource::kTouch);

  EXPECT_EQ(source_window_delegate.motion_event.GetAction(),
            ui::MotionEvent::Action::CANCEL);

  drag_drop_controller->DragCancel();
}

TEST_F(DragDropCaptureDelegateTest, ReleaseCapture) {
  TestWindowDelegate source_window_delegate;

  auto source_window = base::WrapUnique(CreateTestWindowInShellWithDelegate(
      &source_window_delegate, -1, gfx::Rect(0, 0, 100, 100)));
  source_window->Show();
  EXPECT_FALSE(source_window_delegate.touch_cancel_received);

  drag_drop_capture_delegate_->TakeCapture(
      source_window->GetRootWindow(), source_window.get(),
      base::BindLambdaForTesting([]() {}),
      ui::TransferTouchesBehavior::kCancel);

  EXPECT_TRUE(ash::window_util::GetCaptureWindow());

  drag_drop_capture_delegate_->ReleaseCapture();

  EXPECT_FALSE(ash::window_util::GetCaptureWindow());
}

}  // namespace ash