Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / menu_view_drag_and_drop_test.cc
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.
4
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"
15
16 namespace {
17
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
26
27 const char kTestNestedDragData[] = "test_nested_drag_data";
28 const char kTestTopLevelDragData[] = "test_top_level_drag_data";
29
30 // A simple view which can be dragged.
31 class TestDragView : public views::View {
32  public:
33   TestDragView();
34   ~TestDragView() override;
35
36  private:
37   // views::View:
38   int GetDragOperations(const gfx::Point& point) override;
39   void WriteDragData(const gfx::Point& point,
40                      ui::OSExchangeData* data) override;
41
42   DISALLOW_COPY_AND_ASSIGN(TestDragView);
43 };
44
45 TestDragView::TestDragView() {
46 }
47
48 TestDragView::~TestDragView() {
49 }
50
51 int TestDragView::GetDragOperations(const gfx::Point& point) {
52   return ui::DragDropTypes::DRAG_MOVE;
53 }
54
55 void TestDragView::WriteDragData(const gfx::Point& point,
56                                  ui::OSExchangeData* data) {
57   data->SetString(base::ASCIIToUTF16(kTestNestedDragData));
58 }
59
60 // A simple view to serve as a drop target.
61 class TestTargetView : public views::View {
62  public:
63   TestTargetView();
64   ~TestTargetView() override;
65
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_; }
71
72  private:
73   // views::View:
74   bool GetDropFormats(
75       int* formats,
76       std::set<OSExchangeData::CustomFormat>* custom_formats) override;
77   bool AreDropTypesRequired() override;
78   bool CanDrop(const OSExchangeData& data) override;
79   void OnDragEntered(const ui::DropTargetEvent& event) override;
80   int OnDragUpdated(const ui::DropTargetEvent& event) override;
81   int OnPerformDrop(const ui::DropTargetEvent& event) override;
82   void OnDragExited() override;
83
84   // Whether or not we are currently dragging.
85   bool dragging_;
86
87   // Whether or not a drop has been performed on the view.
88   bool dropped_;
89
90   DISALLOW_COPY_AND_ASSIGN(TestTargetView);
91 };
92
93 TestTargetView::TestTargetView() : dragging_(false), dropped_(false) {
94 }
95
96 void TestTargetView::Init(views::View* parent) {
97   // First, match the parent's size.
98   SetSize(parent->size());
99
100   // Then add two draggable views, each 10x2.
101   views::View* first = new TestDragView();
102   AddChildView(first);
103   first->SetBounds(2, 2, 10, 2);
104
105   views::View* second = new TestDragView();
106   AddChildView(second);
107   second->SetBounds(15, 2, 10, 2);
108 }
109
110 TestTargetView::~TestTargetView() {
111 }
112
113 bool TestTargetView::GetDropFormats(
114     int* formats, std::set<OSExchangeData::CustomFormat>* custom_formats) {
115   *formats = ui::OSExchangeData::STRING;
116   return true;
117 }
118
119 bool TestTargetView::AreDropTypesRequired() {
120   return true;
121 }
122
123 bool TestTargetView::CanDrop(const OSExchangeData& data) {
124   base::string16 contents;
125   return data.GetString(&contents) &&
126          contents == base::ASCIIToUTF16(kTestNestedDragData);
127 }
128
129 void TestTargetView::OnDragEntered(const ui::DropTargetEvent& event) {
130   dragging_ = true;
131 }
132
133 int TestTargetView::OnDragUpdated(const ui::DropTargetEvent& event) {
134   return ui::DragDropTypes::DRAG_MOVE;
135 }
136
137 int TestTargetView::OnPerformDrop(const ui::DropTargetEvent& event) {
138   dragging_ = false;
139   dropped_ = true;
140   return ui::DragDropTypes::DRAG_MOVE;
141 }
142
143 void TestTargetView::OnDragExited() {
144   dragging_ = false;
145 }
146
147 }  // namespace
148
149 class MenuViewDragAndDropTest : public MenuTestBase {
150  public:
151   MenuViewDragAndDropTest();
152   ~MenuViewDragAndDropTest() override;
153
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_; }
158
159  private:
160   // MenuTestBase:
161   void BuildMenu(views::MenuItemView* menu) override;
162
163   // views::MenuDelegate:
164   bool GetDropFormats(
165       views::MenuItemView* menu,
166       int* formats,
167       std::set<ui::OSExchangeData::CustomFormat>* custom_formats) override;
168   bool AreDropTypesRequired(views::MenuItemView* menu) override;
169   bool CanDrop(views::MenuItemView* menu,
170                const ui::OSExchangeData& data) override;
171   int GetDropOperation(views::MenuItemView* item,
172                        const ui::DropTargetEvent& event,
173                        DropPosition* position) override;
174   int OnPerformDrop(views::MenuItemView* menu,
175                     DropPosition position,
176                     const ui::DropTargetEvent& event) override;
177   bool CanDrag(views::MenuItemView* menu) override;
178   void WriteDragData(views::MenuItemView* sender,
179                      ui::OSExchangeData* data) override;
180   int GetDragOperations(views::MenuItemView* sender) override;
181   bool ShouldCloseOnDragComplete() override;
182
183   // The special view in the menu, which supports its own drag and drop.
184   TestTargetView* target_view_;
185
186   // Whether or not we have been asked to close on drag complete.
187   bool asked_to_close_;
188
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_;
192
193   DISALLOW_COPY_AND_ASSIGN(MenuViewDragAndDropTest);
194 };
195
196 MenuViewDragAndDropTest::MenuViewDragAndDropTest()
197     : target_view_(NULL),
198       asked_to_close_(false),
199       performed_in_menu_drop_(false) {
200 }
201
202 MenuViewDragAndDropTest::~MenuViewDragAndDropTest() {
203 }
204
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"));
217 }
218
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;
225 }
226
227 bool MenuViewDragAndDropTest::AreDropTypesRequired(views::MenuItemView* menu) {
228   return true;
229 }
230
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);
236 }
237
238 int MenuViewDragAndDropTest::GetDropOperation(views::MenuItemView* item,
239                                               const ui::DropTargetEvent& event,
240                                               DropPosition* position) {
241   return ui::DragDropTypes::DRAG_MOVE;
242 }
243
244
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;
250 }
251
252 bool MenuViewDragAndDropTest::CanDrag(views::MenuItemView* menu) {
253   return true;
254 }
255
256 void MenuViewDragAndDropTest::WriteDragData(
257     views::MenuItemView* sender, ui::OSExchangeData* data) {
258   data->SetString(base::ASCIIToUTF16(kTestTopLevelDragData));
259 }
260
261 int MenuViewDragAndDropTest::GetDragOperations(views::MenuItemView* sender) {
262   return ui::DragDropTypes::DRAG_MOVE;
263 }
264
265 bool MenuViewDragAndDropTest::ShouldCloseOnDragComplete() {
266   asked_to_close_ = true;
267   return false;
268 }
269
270 class MenuViewDragAndDropTestTestInMenuDrag : public MenuViewDragAndDropTest {
271  public:
272   MenuViewDragAndDropTestTestInMenuDrag() {}
273   ~MenuViewDragAndDropTestTestInMenuDrag() override {}
274
275  private:
276   // MenuViewDragAndDropTest:
277   void DoTestWithMenuOpen() override;
278
279   void Step2();
280   void Step3();
281   void Step4();
282 };
283
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());
290
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));
294
295   // We're going to drag the second menu element.
296   views::MenuItemView* drag_view = submenu->GetMenuItemAt(1);
297   ASSERT_TRUE(drag_view != NULL);
298
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));
305 }
306
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);
311
312   // Start a drag.
313   ui_controls::SendMouseMoveNotifyWhenDone(
314       loc.x() + 10,
315       loc.y(),
316       CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::Step3));
317
318   ScheduleMouseMoveInBackground(loc.x(), loc.y());
319 }
320
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());
327
328   ui_controls::SendMouseEventsNotifyWhenDone(
329       ui_controls::LEFT,
330       ui_controls::UP,
331       CreateEventTask(this, &MenuViewDragAndDropTestTestInMenuDrag::Step4));
332 }
333
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());
344
345   Done();
346 }
347
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
354
355 class MenuViewDragAndDropTestNestedDrag : public MenuViewDragAndDropTest {
356  public:
357   MenuViewDragAndDropTestNestedDrag() {}
358   ~MenuViewDragAndDropTestNestedDrag() override {}
359
360  private:
361   // MenuViewDragAndDropTest:
362   void DoTestWithMenuOpen() override;
363
364   void Step2();
365   void Step3();
366   void Step4();
367 };
368
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());
381
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));
385
386   // The target view should now have two children.
387   ASSERT_EQ(2, target_view()->child_count());
388
389   views::View* drag_view = target_view()->child_at(0);
390   ASSERT_TRUE(drag_view != NULL);
391
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));
398 }
399
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);
404
405   // Start a drag.
406   ui_controls::SendMouseMoveNotifyWhenDone(
407       loc.x() + 3,
408       loc.y(),
409       CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::Step3));
410
411   ScheduleMouseMoveInBackground(loc.x(), loc.y());
412 }
413
414 void MenuViewDragAndDropTestNestedDrag::Step3() {
415   // The view should be dragging now.
416   EXPECT_TRUE(target_view()->dragging());
417
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());
423
424   ui_controls::SendMouseEventsNotifyWhenDone(
425       ui_controls::LEFT,
426       ui_controls::UP,
427       CreateEventTask(this, &MenuViewDragAndDropTestNestedDrag::Step4));
428 }
429
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());
441
442   // Clean up.
443   menu()->GetSubmenu()->Close();
444
445   Done();
446 }
447
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
455
456 class MenuViewDragAndDropForDropStayOpen : public MenuViewDragAndDropTest {
457  public:
458   MenuViewDragAndDropForDropStayOpen() {}
459   ~MenuViewDragAndDropForDropStayOpen() override {}
460
461  private:
462   // MenuViewDragAndDropTest:
463   int GetMenuRunnerFlags() override;
464   void DoTestWithMenuOpen() override;
465 };
466
467 int MenuViewDragAndDropForDropStayOpen::GetMenuRunnerFlags() {
468   return views::MenuRunner::HAS_MNEMONICS |
469          views::MenuRunner::NESTED_DRAG |
470          views::MenuRunner::FOR_DROP;
471 }
472
473 void MenuViewDragAndDropForDropStayOpen::DoTestWithMenuOpen() {
474   views::SubmenuView* submenu = menu()->GetSubmenu();
475   ASSERT_TRUE(submenu);
476   ASSERT_TRUE(submenu->IsShowing());
477
478   views::MenuController* controller = menu()->GetMenuController();
479   ASSERT_TRUE(controller);
480   EXPECT_FALSE(controller->IsCancelAllTimerRunningForTest());
481
482   Done();
483 }
484
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)
488
489 class MenuViewDragAndDropForDropCancel : public MenuViewDragAndDropTest {
490  public:
491   MenuViewDragAndDropForDropCancel() {}
492   ~MenuViewDragAndDropForDropCancel() override {}
493
494  private:
495   // MenuViewDragAndDropTest:
496   int GetMenuRunnerFlags() override;
497   void DoTestWithMenuOpen() override;
498 };
499
500 int MenuViewDragAndDropForDropCancel::GetMenuRunnerFlags() {
501   return views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::FOR_DROP;
502 }
503
504 void MenuViewDragAndDropForDropCancel::DoTestWithMenuOpen() {
505   views::SubmenuView* submenu = menu()->GetSubmenu();
506   ASSERT_TRUE(submenu);
507   ASSERT_TRUE(submenu->IsShowing());
508
509   views::MenuController* controller = menu()->GetMenuController();
510   ASSERT_TRUE(controller);
511   EXPECT_TRUE(controller->IsCancelAllTimerRunningForTest());
512
513   Done();
514 }
515
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)