blob: 17a6c4146fd5b076f023030727b76592f10f87b3 [file] [log] [blame]
// Copyright 2014 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 "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/views/menu_test_base.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/view.h"
namespace {
// Borrowed from chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc,
// since these are also disabled on Linux for drag and drop.
// TODO(erg): Fix DND tests on linux_aura. crbug.com/163931
#if defined(OS_LINUX) && defined(USE_AURA)
#define MAYBE(x) DISABLED_##x
#else
#define MAYBE(x) x
#endif
const char kTestNestedDragData[] = "test_nested_drag_data";
const char kTestTopLevelDragData[] = "test_top_level_drag_data";
// A simple view which can be dragged.
class TestDragView : public views::View {
public:
TestDragView();
virtual ~TestDragView();
private:
// views::View:
virtual int GetDragOperations(const gfx::Point& point) OVERRIDE;
virtual void WriteDragData(const gfx::Point& point,
ui::OSExchangeData* data) OVERRIDE;
DISALLOW_COPY_AND_ASSIGN(TestDragView);
};
TestDragView::TestDragView() {
}
TestDragView::~TestDragView() {
}
int TestDragView::GetDragOperations(const gfx::Point& point) {
return ui::DragDropTypes::DRAG_MOVE;
}
void TestDragView::WriteDragData(const gfx::Point& point,
ui::OSExchangeData* data) {
data->SetString(base::ASCIIToUTF16(kTestNestedDragData));
}
// A simple view to serve as a drop target.
class TestTargetView : public views::View {
public:
TestTargetView();
virtual ~TestTargetView();
// Initializes this view to have the same bounds as |parent| and two draggable
// child views.
void Init(views::View* parent);
bool dragging() const { return dragging_; }
bool dropped() const { return dropped_; }
private:
// views::View:
virtual bool GetDropFormats(
int* formats,
std::set<OSExchangeData::CustomFormat>* custom_formats) OVERRIDE;
virtual bool AreDropTypesRequired() OVERRIDE;
virtual bool CanDrop(const OSExchangeData& data) OVERRIDE;
virtual void OnDragEntered(const ui::DropTargetEvent& event) OVERRIDE;
virtual int OnDragUpdated(const ui::DropTargetEvent& event) OVERRIDE;
virtual int OnPerformDrop(const ui::DropTargetEvent& event) OVERRIDE;
virtual void OnDragExited() OVERRIDE;
// Whether or not we are currently dragging.
bool dragging_;
// Whether or not a drop has been performed on the view.
bool dropped_;
DISALLOW_COPY_AND_ASSIGN(TestTargetView);
};
TestTargetView::TestTargetView() : dragging_(false), dropped_(false) {
}
void TestTargetView::Init(views::View* parent) {
// First, match the parent's size.
SetSize(parent->size());
// Then add two draggable views, each 10x2.
views::View* first = new TestDragView();
AddChildView(first);
first->SetBounds(2, 2, 10, 2);
views::View* second = new TestDragView();
AddChildView(second);
second->SetBounds(15, 2, 10, 2);
}
TestTargetView::~TestTargetView() {
}
bool TestTargetView::GetDropFormats(
int* formats, std::set<OSExchangeData::CustomFormat>* custom_formats) {
*formats = ui::OSExchangeData::STRING;
return true;
}
bool TestTargetView::AreDropTypesRequired() {
return true;
}
bool TestTargetView::CanDrop(const OSExchangeData& data) {
base::string16 contents;
return data.GetString(&contents) &&
contents == base::ASCIIToUTF16(kTestNestedDragData);
}
void TestTargetView::OnDragEntered(const ui::DropTargetEvent& event) {
dragging_ = true;
}
int TestTargetView::OnDragUpdated(const ui::DropTargetEvent& event) {
return ui::DragDropTypes::DRAG_MOVE;
}
int TestTargetView::OnPerformDrop(const ui::DropTargetEvent& event) {
dragging_ = false;
dropped_ = true;
return ui::DragDropTypes::DRAG_MOVE;
}
void TestTargetView::OnDragExited() {
dragging_ = false;
}
} // namespace
class MenuViewDragAndDropTest : public MenuTestBase {
public:
MenuViewDragAndDropTest();
virtual ~MenuViewDragAndDropTest();
protected:
TestTargetView* target_view() { return target_view_; }
bool asked_to_close() const { return asked_to_close_; }
bool performed_in_menu_drop() const { return performed_in_menu_drop_; }
private:
// MenuTestBase:
virtual void BuildMenu(views::MenuItemView* menu) OVERRIDE;
// views::MenuDelegate:
virtual bool GetDropFormats(
views::MenuItemView* menu,
int* formats,
std::set<ui::OSExchangeData::CustomFormat>* custom_formats) OVERRIDE;
virtual bool AreDropTypesRequired(views::MenuItemView* menu) OVERRIDE;
virtual bool CanDrop(views::MenuItemView* menu,
const ui::OSExchangeData& data) OVERRIDE;
virtual int GetDropOperation(views::MenuItemView* item,
const ui::DropTargetEvent& event,
DropPosition* position) OVERRIDE;
virtual int OnPerformDrop(views::MenuItemView* menu,
DropPosition position,
const ui::DropTargetEvent& event) OVERRIDE;
virtual bool CanDrag(views::MenuItemView* menu) OVERRIDE;
virtual void WriteDragData(views::MenuItemView* sender,
ui::OSExchangeData* data) OVERRIDE;
virtual int GetDragOperations(views::MenuItemView* sender) OVERRIDE;
virtual bool ShouldCloseOnDragComplete() OVERRIDE;
// The special view in the menu, which supports its own drag and drop.
TestTargetView* target_view_;
// Whether or not we have been asked to close on drag complete.
bool asked_to_close_;
// Whether or not a drop was performed in-menu (i.e., not including drops
// in separate child views).
bool performed_in_menu_drop_;
DISALLOW_COPY_AND_ASSIGN(MenuViewDragAndDropTest);
};
MenuViewDragAndDropTest::MenuViewDragAndDropTest()
: target_view_(NULL),
asked_to_close_(false),
performed_in_menu_drop_(false) {
}
MenuViewDragAndDropTest::~MenuViewDragAndDropTest() {
}
void MenuViewDragAndDropTest::BuildMenu(views::MenuItemView* menu) {
// Build a menu item that has a nested view that supports its own drag and
// drop...
views::MenuItemView* menu_item_view =
menu->AppendMenuItem(1,
base::ASCIIToUTF16("item 1"),
views::MenuItemView::NORMAL);
target_view_ = new TestTargetView();
menu_item_view->AddChildView(target_view_);
// ... as well as two other, normal items.
menu->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("item 2"));
menu->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("item 3"));
}
bool MenuViewDragAndDropTest::GetDropFormats(
views::MenuItemView* menu,
int* formats,
std::set<ui::OSExchangeData::CustomFormat>* custom_formats) {
*formats = ui::OSExchangeData::STRING;
return true;
}
bool MenuViewDragAndDropTest::AreDropTypesRequired(views::MenuItemView* menu) {
return true;
}
bool MenuViewDragAndDropTest::CanDrop(views::MenuItemView* menu,
const ui::OSExchangeData& data) {
base::string16 contents;
return data.GetString(&contents) &&
contents == base::ASCIIToUTF16(kTestTopLevelDragData);
}
int MenuViewDragAndDropTest::GetDropOperation(views::MenuItemView* item,
const ui::DropTargetEvent& event,
DropPosition* position) {
return ui::DragDropTypes::DRAG_MOVE;
}
int MenuViewDragAndDropTest::OnPerformDrop(views::MenuItemView* menu,
DropPosition position,
const ui::DropTargetEvent& event) {
performed_in_menu_drop_ = true;
return ui::DragDropTypes::DRAG_MOVE;
}
bool MenuViewDragAndDropTest::CanDrag(views::MenuItemView* menu) {
return true;
}
void MenuViewDragAndDropTest::WriteDragData(
views::MenuItemView* sender, ui::OSExchangeData* data) {
data->SetString(base::ASCIIToUTF16(kTestTopLevelDragData));
}
int MenuViewDragAndDropTest::GetDragOperations(views::MenuItemView* sender) {
return ui::DragDropTypes::DRAG_MOVE;
}
bool MenuViewDragAndDropTest::ShouldCloseOnDragComplete() {
asked_to_close_ = true;
return false;
}
class MenuViewDragAndDropTestTestInMenuDrag : public MenuViewDragAndDropTest {
public:
MenuViewDragAndDropTestTestInMenuDrag() {}
virtual ~MenuViewDragAndDropTestTestInMenuDrag() {}
private:
// MenuViewDragAndDropTest:
virtual void DoTestWithMenuOpen() OVERRIDE;
void Step2();
void Step3();
void Step4();
};
void MenuViewDragAndDropTestTestInMenuDrag::DoTestWithMenuOpen() {
// A few sanity checks to make sure the menu built correctly.
views::SubmenuView* submenu = menu()->GetSubmenu();
ASSERT_TRUE(submenu);
ASSERT_TRUE(submenu->IsShowing());
ASSERT_EQ(3, submenu->GetMenuItemCount());
// We do this here (instead of in BuildMenu()) so that the menu is already
// built and the bounds are correct.
target_view()->Init(submenu->GetMenuItemAt(0));
// We're going to drag the second menu element.
views::MenuItemView* drag_view = submenu->GetMenuItemAt(1);
ASSERT_TRUE(drag_view != NULL);
// Move mouse to center of menu and press button.
ui_test_utils::MoveMouseToCenterAndPress(
drag_view,
ui_controls::LEFT,
ui_controls::DOWN,
CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::Step2));
}
void MenuViewDragAndDropTestTestInMenuDrag::Step2() {
views::MenuItemView* drop_target = menu()->GetSubmenu()->GetMenuItemAt(2);
gfx::Point loc(1, drop_target->height() - 1);
views::View::ConvertPointToScreen(drop_target, &loc);
// Start a drag.
ui_controls::SendMouseMoveNotifyWhenDone(
loc.x() + 10,
loc.y(),
CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::Step3));
ScheduleMouseMoveInBackground(loc.x(), loc.y());
}
void MenuViewDragAndDropTestTestInMenuDrag::Step3() {
// Drop the item on the target.
views::MenuItemView* drop_target = menu()->GetSubmenu()->GetMenuItemAt(2);
gfx::Point loc(1, drop_target->height() - 2);
views::View::ConvertPointToScreen(drop_target, &loc);
ui_controls::SendMouseMove(loc.x(), loc.y());
ui_controls::SendMouseEventsNotifyWhenDone(
ui_controls::LEFT,
ui_controls::UP,
CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::Step4));
}
void MenuViewDragAndDropTestTestInMenuDrag::Step4() {
// Verify our state.
// We should have performed an in-menu drop, and the nested view should not
// have had a drag and drop. Since the drag happened in menu code, the
// delegate should not have been asked whether or not to close, and the menu
// should simply be closed.
EXPECT_TRUE(performed_in_menu_drop());
EXPECT_FALSE(target_view()->dropped());
EXPECT_FALSE(asked_to_close());
EXPECT_FALSE(menu()->GetSubmenu()->IsShowing());
Done();
}
VIEW_TEST(MenuViewDragAndDropTestTestInMenuDrag, MAYBE(TestInMenuDrag))
class MenuViewDragAndDropTestNestedDrag : public MenuViewDragAndDropTest {
public:
MenuViewDragAndDropTestNestedDrag() {}
virtual ~MenuViewDragAndDropTestNestedDrag() {}
private:
// MenuViewDragAndDropTest:
virtual void DoTestWithMenuOpen() OVERRIDE;
void Step2();
void Step3();
void Step4();
};
void MenuViewDragAndDropTestNestedDrag::DoTestWithMenuOpen() {
// Sanity checks: We should be showing the menu, it should have three
// children, and the first of those children should have a nested view of the
// TestTargetView.
views::SubmenuView* submenu = menu()->GetSubmenu();
ASSERT_TRUE(submenu);
ASSERT_TRUE(submenu->IsShowing());
ASSERT_EQ(3, submenu->GetMenuItemCount());
views::View* first_view = submenu->GetMenuItemAt(0);
ASSERT_EQ(1, first_view->child_count());
views::View* child_view = first_view->child_at(0);
ASSERT_EQ(child_view, target_view());
// We do this here (instead of in BuildMenu()) so that the menu is already
// built and the bounds are correct.
target_view()->Init(submenu->GetMenuItemAt(0));
// The target view should now have two children.
ASSERT_EQ(2, target_view()->child_count());
views::View* drag_view = target_view()->child_at(0);
ASSERT_TRUE(drag_view != NULL);
// Move mouse to center of menu and press button.
ui_test_utils::MoveMouseToCenterAndPress(
drag_view,
ui_controls::LEFT,
ui_controls::DOWN,
CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::Step2));
}
void MenuViewDragAndDropTestNestedDrag::Step2() {
views::View* drop_target = target_view()->child_at(1);
gfx::Point loc(2, 0);
views::View::ConvertPointToScreen(drop_target, &loc);
// Start a drag.
ui_controls::SendMouseMoveNotifyWhenDone(
loc.x() + 3,
loc.y(),
CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::Step3));
ScheduleMouseMoveInBackground(loc.x(), loc.y());
}
void MenuViewDragAndDropTestNestedDrag::Step3() {
// The view should be dragging now.
EXPECT_TRUE(target_view()->dragging());
// Drop the item so that it's now the second item.
views::View* drop_target = target_view()->child_at(1);
gfx::Point loc(5, 0);
views::View::ConvertPointToScreen(drop_target, &loc);
ui_controls::SendMouseMove(loc.x(), loc.y());
ui_controls::SendMouseEventsNotifyWhenDone(
ui_controls::LEFT,
ui_controls::UP,
CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::Step4));
}
void MenuViewDragAndDropTestNestedDrag::Step4() {
// Check our state.
// The target view should have finished its drag, and should have dropped the
// view. The main menu should not have done any drag, and the delegate should
// have been asked if it wanted to close. Since the delegate did not want to
// close, the menu should still be open.
EXPECT_FALSE(target_view()->dragging());
EXPECT_TRUE(target_view()->dropped());
EXPECT_FALSE(performed_in_menu_drop());
EXPECT_TRUE(asked_to_close());
EXPECT_TRUE(menu()->GetSubmenu()->IsShowing());
// Clean up.
menu()->GetSubmenu()->Close();
Done();
}
VIEW_TEST(MenuViewDragAndDropTestNestedDrag,
MAYBE(MenuViewDragAndDropNestedDrag))