Index: chrome/browser/ui/views/menu_view_drag_and_drop_test.cc |
diff --git a/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc b/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..17a6c4146fd5b076f023030727b76592f10f87b3 |
--- /dev/null |
+++ b/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc |
@@ -0,0 +1,442 @@ |
+// 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)) |