Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / ui / views / menu_view_drag_and_drop_test.cc
blob3f6dfb5396faa20bcba54d316f1eefa4f214df8d
1 // Copyright 2014 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.
5 #include "base/strings/utf_string_conversions.h"
6 #include "chrome/browser/ui/views/menu_test_base.h"
7 #include "chrome/test/base/interactive_test_utils.h"
8 #include "ui/base/dragdrop/drag_drop_types.h"
9 #include "ui/base/dragdrop/os_exchange_data.h"
10 #include "ui/views/controls/menu/menu_controller.h"
11 #include "ui/views/controls/menu/menu_item_view.h"
12 #include "ui/views/controls/menu/menu_runner.h"
13 #include "ui/views/controls/menu/submenu_view.h"
14 #include "ui/views/view.h"
16 namespace {
18 // Borrowed from chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc,
19 // since these are also disabled on Linux for drag and drop.
20 // TODO(erg): Fix DND tests on linux_aura. crbug.com/163931
21 #if defined(OS_LINUX) && defined(USE_AURA)
22 #define MAYBE(x) DISABLED_##x
23 #else
24 #define MAYBE(x) x
25 #endif
27 const char kTestNestedDragData[] = "test_nested_drag_data";
28 const char kTestTopLevelDragData[] = "test_top_level_drag_data";
30 // A simple view which can be dragged.
31 class TestDragView : public views::View {
32 public:
33 TestDragView();
34 virtual ~TestDragView();
36 private:
37 // views::View:
38 virtual int GetDragOperations(const gfx::Point& point) override;
39 virtual void WriteDragData(const gfx::Point& point,
40 ui::OSExchangeData* data) override;
42 DISALLOW_COPY_AND_ASSIGN(TestDragView);
45 TestDragView::TestDragView() {
48 TestDragView::~TestDragView() {
51 int TestDragView::GetDragOperations(const gfx::Point& point) {
52 return ui::DragDropTypes::DRAG_MOVE;
55 void TestDragView::WriteDragData(const gfx::Point& point,
56 ui::OSExchangeData* data) {
57 data->SetString(base::ASCIIToUTF16(kTestNestedDragData));
60 // A simple view to serve as a drop target.
61 class TestTargetView : public views::View {
62 public:
63 TestTargetView();
64 virtual ~TestTargetView();
66 // Initializes this view to have the same bounds as |parent| and two draggable
67 // child views.
68 void Init(views::View* parent);
69 bool dragging() const { return dragging_; }
70 bool dropped() const { return dropped_; }
72 private:
73 // views::View:
74 virtual bool GetDropFormats(
75 int* formats,
76 std::set<OSExchangeData::CustomFormat>* custom_formats) override;
77 virtual bool AreDropTypesRequired() override;
78 virtual bool CanDrop(const OSExchangeData& data) override;
79 virtual void OnDragEntered(const ui::DropTargetEvent& event) override;
80 virtual int OnDragUpdated(const ui::DropTargetEvent& event) override;
81 virtual int OnPerformDrop(const ui::DropTargetEvent& event) override;
82 virtual void OnDragExited() override;
84 // Whether or not we are currently dragging.
85 bool dragging_;
87 // Whether or not a drop has been performed on the view.
88 bool dropped_;
90 DISALLOW_COPY_AND_ASSIGN(TestTargetView);
93 TestTargetView::TestTargetView() : dragging_(false), dropped_(false) {
96 void TestTargetView::Init(views::View* parent) {
97 // First, match the parent's size.
98 SetSize(parent->size());
100 // Then add two draggable views, each 10x2.
101 views::View* first = new TestDragView();
102 AddChildView(first);
103 first->SetBounds(2, 2, 10, 2);
105 views::View* second = new TestDragView();
106 AddChildView(second);
107 second->SetBounds(15, 2, 10, 2);
110 TestTargetView::~TestTargetView() {
113 bool TestTargetView::GetDropFormats(
114 int* formats, std::set<OSExchangeData::CustomFormat>* custom_formats) {
115 *formats = ui::OSExchangeData::STRING;
116 return true;
119 bool TestTargetView::AreDropTypesRequired() {
120 return true;
123 bool TestTargetView::CanDrop(const OSExchangeData& data) {
124 base::string16 contents;
125 return data.GetString(&contents) &&
126 contents == base::ASCIIToUTF16(kTestNestedDragData);
129 void TestTargetView::OnDragEntered(const ui::DropTargetEvent& event) {
130 dragging_ = true;
133 int TestTargetView::OnDragUpdated(const ui::DropTargetEvent& event) {
134 return ui::DragDropTypes::DRAG_MOVE;
137 int TestTargetView::OnPerformDrop(const ui::DropTargetEvent& event) {
138 dragging_ = false;
139 dropped_ = true;
140 return ui::DragDropTypes::DRAG_MOVE;
143 void TestTargetView::OnDragExited() {
144 dragging_ = false;
147 } // namespace
149 class MenuViewDragAndDropTest : public MenuTestBase {
150 public:
151 MenuViewDragAndDropTest();
152 virtual ~MenuViewDragAndDropTest();
154 protected:
155 TestTargetView* target_view() { return target_view_; }
156 bool asked_to_close() const { return asked_to_close_; }
157 bool performed_in_menu_drop() const { return performed_in_menu_drop_; }
159 private:
160 // MenuTestBase:
161 virtual void BuildMenu(views::MenuItemView* menu) override;
163 // views::MenuDelegate:
164 virtual bool GetDropFormats(
165 views::MenuItemView* menu,
166 int* formats,
167 std::set<ui::OSExchangeData::CustomFormat>* custom_formats) override;
168 virtual bool AreDropTypesRequired(views::MenuItemView* menu) override;
169 virtual bool CanDrop(views::MenuItemView* menu,
170 const ui::OSExchangeData& data) override;
171 virtual int GetDropOperation(views::MenuItemView* item,
172 const ui::DropTargetEvent& event,
173 DropPosition* position) override;
174 virtual int OnPerformDrop(views::MenuItemView* menu,
175 DropPosition position,
176 const ui::DropTargetEvent& event) override;
177 virtual bool CanDrag(views::MenuItemView* menu) override;
178 virtual void WriteDragData(views::MenuItemView* sender,
179 ui::OSExchangeData* data) override;
180 virtual int GetDragOperations(views::MenuItemView* sender) override;
181 virtual bool ShouldCloseOnDragComplete() override;
183 // The special view in the menu, which supports its own drag and drop.
184 TestTargetView* target_view_;
186 // Whether or not we have been asked to close on drag complete.
187 bool asked_to_close_;
189 // Whether or not a drop was performed in-menu (i.e., not including drops
190 // in separate child views).
191 bool performed_in_menu_drop_;
193 DISALLOW_COPY_AND_ASSIGN(MenuViewDragAndDropTest);
196 MenuViewDragAndDropTest::MenuViewDragAndDropTest()
197 : target_view_(NULL),
198 asked_to_close_(false),
199 performed_in_menu_drop_(false) {
202 MenuViewDragAndDropTest::~MenuViewDragAndDropTest() {
205 void MenuViewDragAndDropTest::BuildMenu(views::MenuItemView* menu) {
206 // Build a menu item that has a nested view that supports its own drag and
207 // drop...
208 views::MenuItemView* menu_item_view =
209 menu->AppendMenuItem(1,
210 base::ASCIIToUTF16("item 1"),
211 views::MenuItemView::NORMAL);
212 target_view_ = new TestTargetView();
213 menu_item_view->AddChildView(target_view_);
214 // ... as well as two other, normal items.
215 menu->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("item 2"));
216 menu->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("item 3"));
219 bool MenuViewDragAndDropTest::GetDropFormats(
220 views::MenuItemView* menu,
221 int* formats,
222 std::set<ui::OSExchangeData::CustomFormat>* custom_formats) {
223 *formats = ui::OSExchangeData::STRING;
224 return true;
227 bool MenuViewDragAndDropTest::AreDropTypesRequired(views::MenuItemView* menu) {
228 return true;
231 bool MenuViewDragAndDropTest::CanDrop(views::MenuItemView* menu,
232 const ui::OSExchangeData& data) {
233 base::string16 contents;
234 return data.GetString(&contents) &&
235 contents == base::ASCIIToUTF16(kTestTopLevelDragData);
238 int MenuViewDragAndDropTest::GetDropOperation(views::MenuItemView* item,
239 const ui::DropTargetEvent& event,
240 DropPosition* position) {
241 return ui::DragDropTypes::DRAG_MOVE;
245 int MenuViewDragAndDropTest::OnPerformDrop(views::MenuItemView* menu,
246 DropPosition position,
247 const ui::DropTargetEvent& event) {
248 performed_in_menu_drop_ = true;
249 return ui::DragDropTypes::DRAG_MOVE;
252 bool MenuViewDragAndDropTest::CanDrag(views::MenuItemView* menu) {
253 return true;
256 void MenuViewDragAndDropTest::WriteDragData(
257 views::MenuItemView* sender, ui::OSExchangeData* data) {
258 data->SetString(base::ASCIIToUTF16(kTestTopLevelDragData));
261 int MenuViewDragAndDropTest::GetDragOperations(views::MenuItemView* sender) {
262 return ui::DragDropTypes::DRAG_MOVE;
265 bool MenuViewDragAndDropTest::ShouldCloseOnDragComplete() {
266 asked_to_close_ = true;
267 return false;
270 class MenuViewDragAndDropTestTestInMenuDrag : public MenuViewDragAndDropTest {
271 public:
272 MenuViewDragAndDropTestTestInMenuDrag() {}
273 virtual ~MenuViewDragAndDropTestTestInMenuDrag() {}
275 private:
276 // MenuViewDragAndDropTest:
277 virtual void DoTestWithMenuOpen() override;
279 void Step2();
280 void Step3();
281 void Step4();
284 void MenuViewDragAndDropTestTestInMenuDrag::DoTestWithMenuOpen() {
285 // A few sanity checks to make sure the menu built correctly.
286 views::SubmenuView* submenu = menu()->GetSubmenu();
287 ASSERT_TRUE(submenu);
288 ASSERT_TRUE(submenu->IsShowing());
289 ASSERT_EQ(3, submenu->GetMenuItemCount());
291 // We do this here (instead of in BuildMenu()) so that the menu is already
292 // built and the bounds are correct.
293 target_view()->Init(submenu->GetMenuItemAt(0));
295 // We're going to drag the second menu element.
296 views::MenuItemView* drag_view = submenu->GetMenuItemAt(1);
297 ASSERT_TRUE(drag_view != NULL);
299 // Move mouse to center of menu and press button.
300 ui_test_utils::MoveMouseToCenterAndPress(
301 drag_view,
302 ui_controls::LEFT,
303 ui_controls::DOWN,
304 CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::Step2));
307 void MenuViewDragAndDropTestTestInMenuDrag::Step2() {
308 views::MenuItemView* drop_target = menu()->GetSubmenu()->GetMenuItemAt(2);
309 gfx::Point loc(1, drop_target->height() - 1);
310 views::View::ConvertPointToScreen(drop_target, &loc);
312 // Start a drag.
313 ui_controls::SendMouseMoveNotifyWhenDone(
314 loc.x() + 10,
315 loc.y(),
316 CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::Step3));
318 ScheduleMouseMoveInBackground(loc.x(), loc.y());
321 void MenuViewDragAndDropTestTestInMenuDrag::Step3() {
322 // Drop the item on the target.
323 views::MenuItemView* drop_target = menu()->GetSubmenu()->GetMenuItemAt(2);
324 gfx::Point loc(1, drop_target->height() - 2);
325 views::View::ConvertPointToScreen(drop_target, &loc);
326 ui_controls::SendMouseMove(loc.x(), loc.y());
328 ui_controls::SendMouseEventsNotifyWhenDone(
329 ui_controls::LEFT,
330 ui_controls::UP,
331 CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::Step4));
334 void MenuViewDragAndDropTestTestInMenuDrag::Step4() {
335 // Verify our state.
336 // We should have performed an in-menu drop, and the nested view should not
337 // have had a drag and drop. Since the drag happened in menu code, the
338 // delegate should not have been asked whether or not to close, and the menu
339 // should simply be closed.
340 EXPECT_TRUE(performed_in_menu_drop());
341 EXPECT_FALSE(target_view()->dropped());
342 EXPECT_FALSE(asked_to_close());
343 EXPECT_FALSE(menu()->GetSubmenu()->IsShowing());
345 Done();
348 // Test that an in-menu (i.e., entirely implemented in the menu code) closes the
349 // menu automatically once the drag is complete, and does not ask the delegate
350 // to stay open.
351 #if !defined(OS_WIN) // flaky http://crbug.com/401226
352 VIEW_TEST(MenuViewDragAndDropTestTestInMenuDrag, MAYBE(TestInMenuDrag))
353 #endif
355 class MenuViewDragAndDropTestNestedDrag : public MenuViewDragAndDropTest {
356 public:
357 MenuViewDragAndDropTestNestedDrag() {}
358 virtual ~MenuViewDragAndDropTestNestedDrag() {}
360 private:
361 // MenuViewDragAndDropTest:
362 virtual void DoTestWithMenuOpen() override;
364 void Step2();
365 void Step3();
366 void Step4();
369 void MenuViewDragAndDropTestNestedDrag::DoTestWithMenuOpen() {
370 // Sanity checks: We should be showing the menu, it should have three
371 // children, and the first of those children should have a nested view of the
372 // TestTargetView.
373 views::SubmenuView* submenu = menu()->GetSubmenu();
374 ASSERT_TRUE(submenu);
375 ASSERT_TRUE(submenu->IsShowing());
376 ASSERT_EQ(3, submenu->GetMenuItemCount());
377 views::View* first_view = submenu->GetMenuItemAt(0);
378 ASSERT_EQ(1, first_view->child_count());
379 views::View* child_view = first_view->child_at(0);
380 ASSERT_EQ(child_view, target_view());
382 // We do this here (instead of in BuildMenu()) so that the menu is already
383 // built and the bounds are correct.
384 target_view()->Init(submenu->GetMenuItemAt(0));
386 // The target view should now have two children.
387 ASSERT_EQ(2, target_view()->child_count());
389 views::View* drag_view = target_view()->child_at(0);
390 ASSERT_TRUE(drag_view != NULL);
392 // Move mouse to center of menu and press button.
393 ui_test_utils::MoveMouseToCenterAndPress(
394 drag_view,
395 ui_controls::LEFT,
396 ui_controls::DOWN,
397 CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::Step2));
400 void MenuViewDragAndDropTestNestedDrag::Step2() {
401 views::View* drop_target = target_view()->child_at(1);
402 gfx::Point loc(2, 0);
403 views::View::ConvertPointToScreen(drop_target, &loc);
405 // Start a drag.
406 ui_controls::SendMouseMoveNotifyWhenDone(
407 loc.x() + 3,
408 loc.y(),
409 CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::Step3));
411 ScheduleMouseMoveInBackground(loc.x(), loc.y());
414 void MenuViewDragAndDropTestNestedDrag::Step3() {
415 // The view should be dragging now.
416 EXPECT_TRUE(target_view()->dragging());
418 // Drop the item so that it's now the second item.
419 views::View* drop_target = target_view()->child_at(1);
420 gfx::Point loc(5, 0);
421 views::View::ConvertPointToScreen(drop_target, &loc);
422 ui_controls::SendMouseMove(loc.x(), loc.y());
424 ui_controls::SendMouseEventsNotifyWhenDone(
425 ui_controls::LEFT,
426 ui_controls::UP,
427 CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::Step4));
430 void MenuViewDragAndDropTestNestedDrag::Step4() {
431 // Check our state.
432 // The target view should have finished its drag, and should have dropped the
433 // view. The main menu should not have done any drag, and the delegate should
434 // have been asked if it wanted to close. Since the delegate did not want to
435 // close, the menu should still be open.
436 EXPECT_FALSE(target_view()->dragging());
437 EXPECT_TRUE(target_view()->dropped());
438 EXPECT_FALSE(performed_in_menu_drop());
439 EXPECT_TRUE(asked_to_close());
440 EXPECT_TRUE(menu()->GetSubmenu()->IsShowing());
442 // Clean up.
443 menu()->GetSubmenu()->Close();
445 Done();
448 // Test that a nested drag (i.e. one via a child view, and not entirely
449 // implemented in menu code) will consult the delegate before closing the view
450 // after the drag.
451 #if !defined(OS_WIN) // http://crbug.com/401226
452 VIEW_TEST(MenuViewDragAndDropTestNestedDrag,
453 MAYBE(MenuViewDragAndDropNestedDrag))
454 #endif
456 class MenuViewDragAndDropForDropStayOpen : public MenuViewDragAndDropTest {
457 public:
458 MenuViewDragAndDropForDropStayOpen() {}
459 virtual ~MenuViewDragAndDropForDropStayOpen() {}
461 private:
462 // MenuViewDragAndDropTest:
463 virtual int GetMenuRunnerFlags() override;
464 virtual void DoTestWithMenuOpen() override;
467 int MenuViewDragAndDropForDropStayOpen::GetMenuRunnerFlags() {
468 return views::MenuRunner::HAS_MNEMONICS |
469 views::MenuRunner::NESTED_DRAG |
470 views::MenuRunner::FOR_DROP;
473 void MenuViewDragAndDropForDropStayOpen::DoTestWithMenuOpen() {
474 views::SubmenuView* submenu = menu()->GetSubmenu();
475 ASSERT_TRUE(submenu);
476 ASSERT_TRUE(submenu->IsShowing());
478 views::MenuController* controller = menu()->GetMenuController();
479 ASSERT_TRUE(controller);
480 EXPECT_FALSE(controller->IsCancelAllTimerRunningForTest());
482 Done();
485 // Test that if a menu is opened for a drop which is handled by a child view
486 // that the menu does not immediately try to close.
487 VIEW_TEST(MenuViewDragAndDropForDropStayOpen, MenuViewStaysOpenForNestedDrag)
489 class MenuViewDragAndDropForDropCancel : public MenuViewDragAndDropTest {
490 public:
491 MenuViewDragAndDropForDropCancel() {}
492 virtual ~MenuViewDragAndDropForDropCancel() {}
494 private:
495 // MenuViewDragAndDropTest:
496 virtual int GetMenuRunnerFlags() override;
497 virtual void DoTestWithMenuOpen() override;
500 int MenuViewDragAndDropForDropCancel::GetMenuRunnerFlags() {
501 return views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::FOR_DROP;
504 void MenuViewDragAndDropForDropCancel::DoTestWithMenuOpen() {
505 views::SubmenuView* submenu = menu()->GetSubmenu();
506 ASSERT_TRUE(submenu);
507 ASSERT_TRUE(submenu->IsShowing());
509 views::MenuController* controller = menu()->GetMenuController();
510 ASSERT_TRUE(controller);
511 EXPECT_TRUE(controller->IsCancelAllTimerRunningForTest());
513 Done();
516 // Test that if a menu is opened for a drop handled entirely by menu code, the
517 // menu will try to close if it does not receive any drag updates.
518 VIEW_TEST(MenuViewDragAndDropForDropCancel, MenuViewCancelsForOwnDrag)