1 // Copyright (c) 2012 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 "ash/drag_drop/drag_drop_controller.h" 6 7 #include "ash/drag_drop/drag_drop_tracker.h" 8 #include "ash/drag_drop/drag_image_view.h" 9 #include "ash/shell.h" 10 #include "ash/test/ash_test_base.h" 11 #include "base/command_line.h" 12 #include "base/location.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "ui/aura/client/capture_client.h" 15 #include "ui/aura/test/event_generator.h" 16 #include "ui/aura/window_event_dispatcher.h" 17 #include "ui/aura/window_tree_host.h" 18 #include "ui/base/clipboard/clipboard.h" 19 #include "ui/base/clipboard/scoped_clipboard_writer.h" 20 #include "ui/base/dragdrop/drag_drop_types.h" 21 #include "ui/base/dragdrop/drag_utils.h" 22 #include "ui/base/dragdrop/os_exchange_data.h" 23 #include "ui/base/ui_base_switches.h" 24 #include "ui/events/event.h" 25 #include "ui/events/event_utils.h" 26 #include "ui/events/gestures/gesture_types.h" 27 #include "ui/events/test/events_test_utils.h" 28 #include "ui/gfx/animation/linear_animation.h" 29 #include "ui/gfx/image/image_skia_rep.h" 30 #include "ui/views/view.h" 31 #include "ui/views/views_delegate.h" 32 #include "ui/views/widget/native_widget_aura.h" 33 #include "ui/views/widget/native_widget_delegate.h" 34 #include "ui/views/widget/widget.h" 35 36 namespace ash { 37 namespace test { 38 39 namespace { 40 41 // A simple view that makes sure RunShellDrag is invoked on mouse drag. 42 class DragTestView : public views::View { 43 public: 44 DragTestView() : views::View() { 45 Reset(); 46 } 47 48 void Reset() { 49 num_drag_enters_ = 0; 50 num_drag_exits_ = 0; 51 num_drag_updates_ = 0; 52 num_drops_ = 0; 53 drag_done_received_ = false; 54 long_tap_received_ = false; 55 } 56 57 int VerticalDragThreshold() { 58 return views::View::GetVerticalDragThreshold(); 59 } 60 61 int HorizontalDragThreshold() { 62 return views::View::GetHorizontalDragThreshold(); 63 } 64 65 int num_drag_enters_; 66 int num_drag_exits_; 67 int num_drag_updates_; 68 int num_drops_; 69 bool drag_done_received_; 70 bool long_tap_received_; 71 72 private: 73 // View overrides: 74 virtual int GetDragOperations(const gfx::Point& press_pt) OVERRIDE { 75 return ui::DragDropTypes::DRAG_COPY; 76 } 77 78 virtual void WriteDragData(const gfx::Point& p, 79 OSExchangeData* data) OVERRIDE { 80 data->SetString(base::UTF8ToUTF16("I am being dragged")); 81 gfx::ImageSkiaRep image_rep(gfx::Size(10, 20), 1.0f); 82 gfx::ImageSkia image_skia(image_rep); 83 84 drag_utils::SetDragImageOnDataObject(image_skia, gfx::Vector2d(), data); 85 } 86 87 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { 88 return true; 89 } 90 91 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 92 if (event->type() == ui::ET_GESTURE_LONG_TAP) 93 long_tap_received_ = true; 94 return; 95 } 96 97 virtual bool GetDropFormats( 98 int* formats, 99 std::set<OSExchangeData::CustomFormat>* custom_formats) OVERRIDE { 100 *formats = ui::OSExchangeData::STRING; 101 return true; 102 } 103 104 virtual bool CanDrop(const OSExchangeData& data) OVERRIDE { 105 return true; 106 } 107 108 virtual void OnDragEntered(const ui::DropTargetEvent& event) OVERRIDE { 109 num_drag_enters_++; 110 } 111 112 virtual int OnDragUpdated(const ui::DropTargetEvent& event) OVERRIDE { 113 num_drag_updates_++; 114 return ui::DragDropTypes::DRAG_COPY; 115 } 116 117 virtual void OnDragExited() OVERRIDE { 118 num_drag_exits_++; 119 } 120 121 virtual int OnPerformDrop(const ui::DropTargetEvent& event) OVERRIDE { 122 num_drops_++; 123 return ui::DragDropTypes::DRAG_COPY; 124 } 125 126 virtual void OnDragDone() OVERRIDE { 127 drag_done_received_ = true; 128 } 129 130 DISALLOW_COPY_AND_ASSIGN(DragTestView); 131 }; 132 133 class CompletableLinearAnimation : public gfx::LinearAnimation { 134 public: 135 CompletableLinearAnimation(int duration, 136 int frame_rate, 137 gfx::AnimationDelegate* delegate) 138 : gfx::LinearAnimation(duration, frame_rate, delegate), 139 duration_(duration) { 140 } 141 142 void Complete() { 143 Step(start_time() + base::TimeDelta::FromMilliseconds(duration_)); 144 } 145 146 private: 147 int duration_; 148 }; 149 150 class TestDragDropController : public DragDropController { 151 public: 152 TestDragDropController() : DragDropController() { Reset(); } 153 154 void Reset() { 155 drag_start_received_ = false; 156 num_drag_updates_ = 0; 157 drop_received_ = false; 158 drag_canceled_ = false; 159 drag_string_.clear(); 160 } 161 162 virtual int StartDragAndDrop( 163 const ui::OSExchangeData& data, 164 aura::Window* root_window, 165 aura::Window* source_window, 166 const gfx::Point& location, 167 int operation, 168 ui::DragDropTypes::DragEventSource source) OVERRIDE { 169 drag_start_received_ = true; 170 data.GetString(&drag_string_); 171 return DragDropController::StartDragAndDrop( 172 data, root_window, source_window, location, operation, source); 173 } 174 175 virtual void DragUpdate(aura::Window* target, 176 const ui::LocatedEvent& event) OVERRIDE { 177 DragDropController::DragUpdate(target, event); 178 num_drag_updates_++; 179 } 180 181 virtual void Drop(aura::Window* target, 182 const ui::LocatedEvent& event) OVERRIDE { 183 DragDropController::Drop(target, event); 184 drop_received_ = true; 185 } 186 187 virtual void DragCancel() OVERRIDE { 188 DragDropController::DragCancel(); 189 drag_canceled_ = true; 190 } 191 192 virtual gfx::LinearAnimation* CreateCancelAnimation( 193 int duration, 194 int frame_rate, 195 gfx::AnimationDelegate* delegate) OVERRIDE { 196 return new CompletableLinearAnimation(duration, frame_rate, delegate); 197 } 198 199 virtual void DoDragCancel(int animation_duration_ms) OVERRIDE { 200 DragDropController::DoDragCancel(animation_duration_ms); 201 drag_canceled_ = true; 202 } 203 204 bool drag_start_received_; 205 int num_drag_updates_; 206 bool drop_received_; 207 bool drag_canceled_; 208 base::string16 drag_string_; 209 210 private: 211 DISALLOW_COPY_AND_ASSIGN(TestDragDropController); 212 }; 213 214 class TestNativeWidgetAura : public views::NativeWidgetAura { 215 public: 216 explicit TestNativeWidgetAura(views::internal::NativeWidgetDelegate* delegate) 217 : NativeWidgetAura(delegate), 218 check_if_capture_lost_(false) { 219 } 220 221 void set_check_if_capture_lost(bool value) { 222 check_if_capture_lost_ = value; 223 } 224 225 virtual void OnCaptureLost() OVERRIDE { 226 DCHECK(!check_if_capture_lost_); 227 views::NativeWidgetAura::OnCaptureLost(); 228 } 229 230 private: 231 bool check_if_capture_lost_; 232 233 DISALLOW_COPY_AND_ASSIGN(TestNativeWidgetAura); 234 }; 235 236 // TODO(sky): this is for debugging, remove when track down failure. 237 void SetCheckIfCaptureLost(views::Widget* widget, bool value) { 238 // On Windows, the DCHECK triggers when running on bot or locally through RDP, 239 // but not when logged in locally. 240 #if !defined(OS_WIN) 241 static_cast<TestNativeWidgetAura*>(widget->native_widget())-> 242 set_check_if_capture_lost(value); 243 #endif 244 } 245 246 views::Widget* CreateNewWidget() { 247 views::Widget* widget = new views::Widget; 248 views::Widget::InitParams params; 249 params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; 250 params.accept_events = true; 251 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 252 params.parent = Shell::GetPrimaryRootWindow(); 253 params.child = true; 254 params.native_widget = new TestNativeWidgetAura(widget); 255 widget->Init(params); 256 widget->Show(); 257 return widget; 258 } 259 260 void AddViewToWidgetAndResize(views::Widget* widget, views::View* view) { 261 if (!widget->GetContentsView()) { 262 views::View* contents_view = new views::View; 263 widget->SetContentsView(contents_view); 264 } 265 266 views::View* contents_view = widget->GetContentsView(); 267 contents_view->AddChildView(view); 268 view->SetBounds(contents_view->width(), 0, 100, 100); 269 gfx::Rect contents_view_bounds = contents_view->bounds(); 270 contents_view_bounds.Union(view->bounds()); 271 contents_view->SetBoundsRect(contents_view_bounds); 272 widget->SetBounds(contents_view_bounds); 273 } 274 275 void DispatchGesture(ui::EventType gesture_type, gfx::Point location) { 276 ui::GestureEvent gesture_event( 277 gesture_type, 278 location.x(), 279 location.y(), 280 0, 281 ui::EventTimeForNow(), 282 ui::GestureEventDetails(gesture_type, 0, 0), 283 1); 284 ui::EventSource* event_source = 285 Shell::GetPrimaryRootWindow()->GetHost()->GetEventSource(); 286 ui::EventSourceTestApi event_source_test(event_source); 287 ui::EventDispatchDetails details = 288 event_source_test.SendEventToProcessor(&gesture_event); 289 CHECK(!details.dispatcher_destroyed); 290 } 291 292 } // namespace 293 294 class DragDropControllerTest : public AshTestBase { 295 public: 296 DragDropControllerTest() : AshTestBase() {} 297 virtual ~DragDropControllerTest() {} 298 299 virtual void SetUp() OVERRIDE { 300 AshTestBase::SetUp(); 301 drag_drop_controller_.reset(new TestDragDropController); 302 drag_drop_controller_->set_should_block_during_drag_drop(false); 303 aura::client::SetDragDropClient(Shell::GetPrimaryRootWindow(), 304 drag_drop_controller_.get()); 305 } 306 307 virtual void TearDown() OVERRIDE { 308 aura::client::SetDragDropClient(Shell::GetPrimaryRootWindow(), NULL); 309 drag_drop_controller_.reset(); 310 AshTestBase::TearDown(); 311 } 312 313 void UpdateDragData(ui::OSExchangeData* data) { 314 drag_drop_controller_->drag_data_ = data; 315 } 316 317 aura::Window* GetDragWindow() { 318 return drag_drop_controller_->drag_window_; 319 } 320 321 aura::Window* GetDragSourceWindow() { 322 return drag_drop_controller_->drag_source_window_; 323 } 324 325 void SetDragSourceWindow(aura::Window* drag_source_window) { 326 drag_drop_controller_->drag_source_window_ = drag_source_window; 327 drag_source_window->AddObserver(drag_drop_controller_.get()); 328 } 329 330 aura::Window* GetDragImageWindow() { 331 return drag_drop_controller_->drag_image_.get() ? 332 drag_drop_controller_->drag_image_->GetWidget()->GetNativeWindow() : 333 NULL; 334 } 335 336 DragDropTracker* drag_drop_tracker() { 337 return drag_drop_controller_->drag_drop_tracker_.get(); 338 } 339 340 void CompleteCancelAnimation() { 341 CompletableLinearAnimation* animation = 342 static_cast<CompletableLinearAnimation*>( 343 drag_drop_controller_->cancel_animation_.get()); 344 animation->Complete(); 345 } 346 347 protected: 348 scoped_ptr<TestDragDropController> drag_drop_controller_; 349 350 private: 351 DISALLOW_COPY_AND_ASSIGN(DragDropControllerTest); 352 }; 353 354 // TODO(win_aura) http://crbug.com/154081 355 #if defined(OS_WIN) 356 #define MAYBE_DragDropInSingleViewTest DISABLED_DragDropInSingleViewTest 357 #else 358 #define MAYBE_DragDropInSingleViewTest DragDropInSingleViewTest 359 #endif 360 TEST_F(DragDropControllerTest, MAYBE_DragDropInSingleViewTest) { 361 scoped_ptr<views::Widget> widget(CreateNewWidget()); 362 DragTestView* drag_view = new DragTestView; 363 AddViewToWidgetAndResize(widget.get(), drag_view); 364 ui::OSExchangeData data; 365 data.SetString(base::UTF8ToUTF16("I am being dragged")); 366 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 367 widget->GetNativeView()); 368 generator.PressLeftButton(); 369 370 int num_drags = 17; 371 SetCheckIfCaptureLost(widget.get(), true); 372 for (int i = 0; i < num_drags; ++i) { 373 // Because we are not doing a blocking drag and drop, the original 374 // OSDragExchangeData object is lost as soon as we return from the drag 375 // initiation in DragDropController::StartDragAndDrop(). Hence we set the 376 // drag_data_ to a fake drag data object that we created. 377 if (i > 0) 378 UpdateDragData(&data); 379 // 7 comes from views::View::GetVerticalDragThreshold()). 380 if (i >= 7) 381 SetCheckIfCaptureLost(widget.get(), false); 382 383 generator.MoveMouseBy(0, 1); 384 385 // Execute any scheduled draws to process deferred mouse events. 386 RunAllPendingInMessageLoop(); 387 } 388 389 generator.ReleaseLeftButton(); 390 391 EXPECT_TRUE(drag_drop_controller_->drag_start_received_); 392 EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(), 393 drag_drop_controller_->num_drag_updates_); 394 EXPECT_TRUE(drag_drop_controller_->drop_received_); 395 EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"), 396 drag_drop_controller_->drag_string_); 397 398 EXPECT_EQ(1, drag_view->num_drag_enters_); 399 EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(), 400 drag_view->num_drag_updates_); 401 EXPECT_EQ(1, drag_view->num_drops_); 402 EXPECT_EQ(0, drag_view->num_drag_exits_); 403 EXPECT_TRUE(drag_view->drag_done_received_); 404 } 405 406 TEST_F(DragDropControllerTest, DragDropWithZeroDragUpdates) { 407 scoped_ptr<views::Widget> widget(CreateNewWidget()); 408 DragTestView* drag_view = new DragTestView; 409 AddViewToWidgetAndResize(widget.get(), drag_view); 410 ui::OSExchangeData data; 411 data.SetString(base::UTF8ToUTF16("I am being dragged")); 412 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 413 widget->GetNativeView()); 414 generator.PressLeftButton(); 415 416 int num_drags = drag_view->VerticalDragThreshold() + 1; 417 for (int i = 0; i < num_drags; ++i) { 418 // Because we are not doing a blocking drag and drop, the original 419 // OSDragExchangeData object is lost as soon as we return from the drag 420 // initiation in DragDropController::StartDragAndDrop(). Hence we set the 421 // drag_data_ to a fake drag data object that we created. 422 if (i > 0) 423 UpdateDragData(&data); 424 generator.MoveMouseBy(0, 1); 425 } 426 427 UpdateDragData(&data); 428 429 generator.ReleaseLeftButton(); 430 431 EXPECT_TRUE(drag_drop_controller_->drag_start_received_); 432 EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold() + 1, 433 drag_drop_controller_->num_drag_updates_); 434 EXPECT_TRUE(drag_drop_controller_->drop_received_); 435 436 EXPECT_EQ(1, drag_view->num_drag_enters_); 437 EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold() + 1, 438 drag_view->num_drag_updates_); 439 EXPECT_EQ(1, drag_view->num_drops_); 440 EXPECT_EQ(0, drag_view->num_drag_exits_); 441 EXPECT_TRUE(drag_view->drag_done_received_); 442 } 443 444 // TODO(win_aura) http://crbug.com/154081 445 #if defined(OS_WIN) 446 #define MAYBE_DragDropInMultipleViewsSingleWidgetTest DISABLED_DragDropInMultipleViewsSingleWidgetTest 447 #else 448 #define MAYBE_DragDropInMultipleViewsSingleWidgetTest DragDropInMultipleViewsSingleWidgetTest 449 #endif 450 TEST_F(DragDropControllerTest, MAYBE_DragDropInMultipleViewsSingleWidgetTest) { 451 scoped_ptr<views::Widget> widget(CreateNewWidget()); 452 DragTestView* drag_view1 = new DragTestView; 453 AddViewToWidgetAndResize(widget.get(), drag_view1); 454 DragTestView* drag_view2 = new DragTestView; 455 AddViewToWidgetAndResize(widget.get(), drag_view2); 456 457 ui::OSExchangeData data; 458 data.SetString(base::UTF8ToUTF16("I am being dragged")); 459 460 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); 461 generator.MoveMouseRelativeTo(widget->GetNativeView(), 462 drag_view1->bounds().CenterPoint()); 463 generator.PressLeftButton(); 464 465 int num_drags = drag_view1->width(); 466 for (int i = 0; i < num_drags; ++i) { 467 // Because we are not doing a blocking drag and drop, the original 468 // OSDragExchangeData object is lost as soon as we return from the drag 469 // initiation in DragDropController::StartDragAndDrop(). Hence we set the 470 // drag_data_ to a fake drag data object that we created. 471 if (i > 0) 472 UpdateDragData(&data); 473 generator.MoveMouseBy(1, 0); 474 475 // Execute any scheduled draws to process deferred mouse events. 476 RunAllPendingInMessageLoop(); 477 } 478 479 generator.ReleaseLeftButton(); 480 481 EXPECT_TRUE(drag_drop_controller_->drag_start_received_); 482 EXPECT_EQ(num_drags - 1 - drag_view1->HorizontalDragThreshold(), 483 drag_drop_controller_->num_drag_updates_); 484 EXPECT_TRUE(drag_drop_controller_->drop_received_); 485 EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"), 486 drag_drop_controller_->drag_string_); 487 488 EXPECT_EQ(1, drag_view1->num_drag_enters_); 489 int num_expected_updates = drag_view1->bounds().width() - 490 drag_view1->bounds().CenterPoint().x() - 2; 491 EXPECT_EQ(num_expected_updates - drag_view1->HorizontalDragThreshold(), 492 drag_view1->num_drag_updates_); 493 EXPECT_EQ(0, drag_view1->num_drops_); 494 EXPECT_EQ(1, drag_view1->num_drag_exits_); 495 EXPECT_TRUE(drag_view1->drag_done_received_); 496 497 EXPECT_EQ(1, drag_view2->num_drag_enters_); 498 num_expected_updates = num_drags - num_expected_updates - 1; 499 EXPECT_EQ(num_expected_updates, drag_view2->num_drag_updates_); 500 EXPECT_EQ(1, drag_view2->num_drops_); 501 EXPECT_EQ(0, drag_view2->num_drag_exits_); 502 EXPECT_FALSE(drag_view2->drag_done_received_); 503 } 504 505 // TODO(win_aura) http://crbug.com/154081 506 #if defined(OS_WIN) 507 #define MAYBE_DragDropInMultipleViewsMultipleWidgetsTest DISABLED_DragDropInMultipleViewsMultipleWidgetsTest 508 #else 509 #define MAYBE_DragDropInMultipleViewsMultipleWidgetsTest DragDropInMultipleViewsMultipleWidgetsTest 510 #endif 511 TEST_F(DragDropControllerTest, MAYBE_DragDropInMultipleViewsMultipleWidgetsTest) { 512 scoped_ptr<views::Widget> widget1(CreateNewWidget()); 513 DragTestView* drag_view1 = new DragTestView; 514 AddViewToWidgetAndResize(widget1.get(), drag_view1); 515 scoped_ptr<views::Widget> widget2(CreateNewWidget()); 516 DragTestView* drag_view2 = new DragTestView; 517 AddViewToWidgetAndResize(widget2.get(), drag_view2); 518 gfx::Rect widget1_bounds = widget1->GetClientAreaBoundsInScreen(); 519 gfx::Rect widget2_bounds = widget2->GetClientAreaBoundsInScreen(); 520 widget2->SetBounds(gfx::Rect(widget1_bounds.width(), 0, 521 widget2_bounds.width(), widget2_bounds.height())); 522 523 ui::OSExchangeData data; 524 data.SetString(base::UTF8ToUTF16("I am being dragged")); 525 526 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 527 widget1->GetNativeView()); 528 generator.PressLeftButton(); 529 530 int num_drags = drag_view1->width(); 531 for (int i = 0; i < num_drags; ++i) { 532 // Because we are not doing a blocking drag and drop, the original 533 // OSDragExchangeData object is lost as soon as we return from the drag 534 // initiation in DragDropController::StartDragAndDrop(). Hence we set the 535 // drag_data_ to a fake drag data object that we created. 536 if (i > 0) 537 UpdateDragData(&data); 538 generator.MoveMouseBy(1, 0); 539 540 // Execute any scheduled draws to process deferred mouse events. 541 RunAllPendingInMessageLoop(); 542 } 543 544 generator.ReleaseLeftButton(); 545 546 EXPECT_TRUE(drag_drop_controller_->drag_start_received_); 547 EXPECT_EQ(num_drags - 1 - drag_view1->HorizontalDragThreshold(), 548 drag_drop_controller_->num_drag_updates_); 549 EXPECT_TRUE(drag_drop_controller_->drop_received_); 550 EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"), 551 drag_drop_controller_->drag_string_); 552 553 EXPECT_EQ(1, drag_view1->num_drag_enters_); 554 int num_expected_updates = drag_view1->bounds().width() - 555 drag_view1->bounds().CenterPoint().x() - 2; 556 EXPECT_EQ(num_expected_updates - drag_view1->HorizontalDragThreshold(), 557 drag_view1->num_drag_updates_); 558 EXPECT_EQ(0, drag_view1->num_drops_); 559 EXPECT_EQ(1, drag_view1->num_drag_exits_); 560 EXPECT_TRUE(drag_view1->drag_done_received_); 561 562 EXPECT_EQ(1, drag_view2->num_drag_enters_); 563 num_expected_updates = num_drags - num_expected_updates - 1; 564 EXPECT_EQ(num_expected_updates, drag_view2->num_drag_updates_); 565 EXPECT_EQ(1, drag_view2->num_drops_); 566 EXPECT_EQ(0, drag_view2->num_drag_exits_); 567 EXPECT_FALSE(drag_view2->drag_done_received_); 568 } 569 570 // TODO(win_aura) http://crbug.com/154081 571 #if defined(OS_WIN) 572 #define MAYBE_ViewRemovedWhileInDragDropTest DISABLED_ViewRemovedWhileInDragDropTest 573 #else 574 #define MAYBE_ViewRemovedWhileInDragDropTest ViewRemovedWhileInDragDropTest 575 #endif 576 TEST_F(DragDropControllerTest, MAYBE_ViewRemovedWhileInDragDropTest) { 577 scoped_ptr<views::Widget> widget(CreateNewWidget()); 578 scoped_ptr<DragTestView> drag_view(new DragTestView); 579 AddViewToWidgetAndResize(widget.get(), drag_view.get()); 580 gfx::Point point = gfx::Rect(drag_view->bounds()).CenterPoint(); 581 ui::OSExchangeData data; 582 data.SetString(base::UTF8ToUTF16("I am being dragged")); 583 584 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); 585 generator.MoveMouseToCenterOf(widget->GetNativeView()); 586 generator.PressLeftButton(); 587 588 int num_drags_1 = 17; 589 for (int i = 0; i < num_drags_1; ++i) { 590 // Because we are not doing a blocking drag and drop, the original 591 // OSDragExchangeData object is lost as soon as we return from the drag 592 // initiation in DragDropController::StartDragAndDrop(). Hence we set the 593 // drag_data_ to a fake drag data object that we created. 594 if (i > 0) 595 UpdateDragData(&data); 596 generator.MoveMouseBy(0, 1); 597 598 // Execute any scheduled draws to process deferred mouse events. 599 RunAllPendingInMessageLoop(); 600 } 601 602 drag_view->parent()->RemoveChildView(drag_view.get()); 603 // View has been removed. We will not get any of the following drag updates. 604 int num_drags_2 = 23; 605 for (int i = 0; i < num_drags_2; ++i) { 606 UpdateDragData(&data); 607 generator.MoveMouseBy(0, 1); 608 609 // Execute any scheduled draws to process deferred mouse events. 610 RunAllPendingInMessageLoop(); 611 } 612 613 generator.ReleaseLeftButton(); 614 615 EXPECT_TRUE(drag_drop_controller_->drag_start_received_); 616 EXPECT_EQ(num_drags_1 + num_drags_2 - 1 - drag_view->VerticalDragThreshold(), 617 drag_drop_controller_->num_drag_updates_); 618 EXPECT_TRUE(drag_drop_controller_->drop_received_); 619 EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"), 620 drag_drop_controller_->drag_string_); 621 622 EXPECT_EQ(1, drag_view->num_drag_enters_); 623 EXPECT_EQ(num_drags_1 - 1 - drag_view->VerticalDragThreshold(), 624 drag_view->num_drag_updates_); 625 EXPECT_EQ(0, drag_view->num_drops_); 626 EXPECT_EQ(0, drag_view->num_drag_exits_); 627 EXPECT_TRUE(drag_view->drag_done_received_); 628 } 629 630 TEST_F(DragDropControllerTest, DragLeavesClipboardAloneTest) { 631 ui::Clipboard* cb = ui::Clipboard::GetForCurrentThread(); 632 std::string clip_str("I am on the clipboard"); 633 { 634 // We first copy some text to the clipboard. 635 ui::ScopedClipboardWriter scw(cb, ui::CLIPBOARD_TYPE_COPY_PASTE); 636 scw.WriteText(base::ASCIIToUTF16(clip_str)); 637 } 638 EXPECT_TRUE(cb->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(), 639 ui::CLIPBOARD_TYPE_COPY_PASTE)); 640 641 scoped_ptr<views::Widget> widget(CreateNewWidget()); 642 DragTestView* drag_view = new DragTestView; 643 AddViewToWidgetAndResize(widget.get(), drag_view); 644 645 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 646 widget->GetNativeView()); 647 ui::OSExchangeData data; 648 std::string data_str("I am being dragged"); 649 data.SetString(base::ASCIIToUTF16(data_str)); 650 651 generator.PressLeftButton(); 652 generator.MoveMouseBy(0, drag_view->VerticalDragThreshold() + 1); 653 654 // Execute any scheduled draws to process deferred mouse events. 655 RunAllPendingInMessageLoop(); 656 657 // Verify the clipboard contents haven't changed 658 std::string result; 659 EXPECT_TRUE(cb->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(), 660 ui::CLIPBOARD_TYPE_COPY_PASTE)); 661 cb->ReadAsciiText(ui::CLIPBOARD_TYPE_COPY_PASTE, &result); 662 EXPECT_EQ(clip_str, result); 663 // Destory the clipboard here because ash doesn't delete it. 664 // crbug.com/158150. 665 ui::Clipboard::DestroyClipboardForCurrentThread(); 666 } 667 668 TEST_F(DragDropControllerTest, WindowDestroyedDuringDragDrop) { 669 scoped_ptr<views::Widget> widget(CreateNewWidget()); 670 DragTestView* drag_view = new DragTestView; 671 AddViewToWidgetAndResize(widget.get(), drag_view); 672 aura::Window* window = widget->GetNativeView(); 673 674 ui::OSExchangeData data; 675 data.SetString(base::UTF8ToUTF16("I am being dragged")); 676 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 677 widget->GetNativeView()); 678 generator.PressLeftButton(); 679 680 int num_drags = 17; 681 for (int i = 0; i < num_drags; ++i) { 682 // Because we are not doing a blocking drag and drop, the original 683 // OSDragExchangeData object is lost as soon as we return from the drag 684 // initiation in DragDropController::StartDragAndDrop(). Hence we set the 685 // drag_data_ to a fake drag data object that we created. 686 if (i > 0) 687 UpdateDragData(&data); 688 generator.MoveMouseBy(0, 1); 689 690 // Execute any scheduled draws to process deferred mouse events. 691 RunAllPendingInMessageLoop(); 692 693 if (i > drag_view->VerticalDragThreshold()) 694 EXPECT_EQ(window, GetDragWindow()); 695 } 696 697 widget->CloseNow(); 698 EXPECT_FALSE(GetDragWindow()); 699 700 num_drags = 23; 701 for (int i = 0; i < num_drags; ++i) { 702 if (i > 0) 703 UpdateDragData(&data); 704 generator.MoveMouseBy(0, 1); 705 // We should not crash here. 706 } 707 708 generator.ReleaseLeftButton(); 709 710 EXPECT_TRUE(drag_drop_controller_->drag_start_received_); 711 EXPECT_TRUE(drag_drop_controller_->drop_received_); 712 } 713 714 TEST_F(DragDropControllerTest, SyntheticEventsDuringDragDrop) { 715 scoped_ptr<views::Widget> widget(CreateNewWidget()); 716 DragTestView* drag_view = new DragTestView; 717 AddViewToWidgetAndResize(widget.get(), drag_view); 718 ui::OSExchangeData data; 719 data.SetString(base::UTF8ToUTF16("I am being dragged")); 720 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 721 widget->GetNativeView()); 722 generator.PressLeftButton(); 723 724 int num_drags = 17; 725 for (int i = 0; i < num_drags; ++i) { 726 // Because we are not doing a blocking drag and drop, the original 727 // OSDragExchangeData object is lost as soon as we return from the drag 728 // initiation in DragDropController::StartDragAndDrop(). Hence we set the 729 // drag_data_ to a fake drag data object that we created. 730 if (i > 0) 731 UpdateDragData(&data); 732 generator.MoveMouseBy(0, 1); 733 734 // We send a unexpected mouse move event. Note that we cannot use 735 // EventGenerator since it implicitly turns these into mouse drag events. 736 // The DragDropController should simply ignore these events. 737 gfx::Point mouse_move_location = drag_view->bounds().CenterPoint(); 738 ui::MouseEvent mouse_move(ui::ET_MOUSE_MOVED, mouse_move_location, 739 mouse_move_location, 0, 0); 740 ui::EventDispatchDetails details = Shell::GetPrimaryRootWindow()-> 741 GetHost()->event_processor()->OnEventFromSource(&mouse_move); 742 ASSERT_FALSE(details.dispatcher_destroyed); 743 } 744 745 generator.ReleaseLeftButton(); 746 747 EXPECT_TRUE(drag_drop_controller_->drag_start_received_); 748 EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(), 749 drag_drop_controller_->num_drag_updates_); 750 EXPECT_TRUE(drag_drop_controller_->drop_received_); 751 EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"), 752 drag_drop_controller_->drag_string_); 753 754 EXPECT_EQ(1, drag_view->num_drag_enters_); 755 EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(), 756 drag_view->num_drag_updates_); 757 EXPECT_EQ(1, drag_view->num_drops_); 758 EXPECT_EQ(0, drag_view->num_drag_exits_); 759 EXPECT_TRUE(drag_view->drag_done_received_); 760 } 761 762 // TODO(win_aura) http://crbug.com/154081 763 #if defined(OS_WIN) 764 #define MAYBE_PressingEscapeCancelsDragDrop DISABLED_PressingEscapeCancelsDragDrop 765 #define MAYBE_CaptureLostCancelsDragDrop DISABLED_CaptureLostCancelsDragDrop 766 #else 767 #define MAYBE_PressingEscapeCancelsDragDrop PressingEscapeCancelsDragDrop 768 #define MAYBE_CaptureLostCancelsDragDrop CaptureLostCancelsDragDrop 769 #endif 770 TEST_F(DragDropControllerTest, MAYBE_PressingEscapeCancelsDragDrop) { 771 scoped_ptr<views::Widget> widget(CreateNewWidget()); 772 DragTestView* drag_view = new DragTestView; 773 AddViewToWidgetAndResize(widget.get(), drag_view); 774 ui::OSExchangeData data; 775 data.SetString(base::UTF8ToUTF16("I am being dragged")); 776 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 777 widget->GetNativeView()); 778 generator.PressLeftButton(); 779 780 int num_drags = 17; 781 for (int i = 0; i < num_drags; ++i) { 782 // Because we are not doing a blocking drag and drop, the original 783 // OSDragExchangeData object is lost as soon as we return from the drag 784 // initiation in DragDropController::StartDragAndDrop(). Hence we set the 785 // drag_data_ to a fake drag data object that we created. 786 if (i > 0) 787 UpdateDragData(&data); 788 generator.MoveMouseBy(0, 1); 789 790 // Execute any scheduled draws to process deferred mouse events. 791 RunAllPendingInMessageLoop(); 792 } 793 794 generator.PressKey(ui::VKEY_ESCAPE, 0); 795 796 EXPECT_TRUE(drag_drop_controller_->drag_start_received_); 797 EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(), 798 drag_drop_controller_->num_drag_updates_); 799 EXPECT_FALSE(drag_drop_controller_->drop_received_); 800 EXPECT_TRUE(drag_drop_controller_->drag_canceled_); 801 EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"), 802 drag_drop_controller_->drag_string_); 803 804 EXPECT_EQ(1, drag_view->num_drag_enters_); 805 EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(), 806 drag_view->num_drag_updates_); 807 EXPECT_EQ(0, drag_view->num_drops_); 808 EXPECT_EQ(1, drag_view->num_drag_exits_); 809 EXPECT_TRUE(drag_view->drag_done_received_); 810 } 811 812 TEST_F(DragDropControllerTest, MAYBE_CaptureLostCancelsDragDrop) { 813 scoped_ptr<views::Widget> widget(CreateNewWidget()); 814 DragTestView* drag_view = new DragTestView; 815 AddViewToWidgetAndResize(widget.get(), drag_view); 816 ui::OSExchangeData data; 817 data.SetString(base::UTF8ToUTF16("I am being dragged")); 818 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 819 widget->GetNativeView()); 820 generator.PressLeftButton(); 821 822 int num_drags = 17; 823 for (int i = 0; i < num_drags; ++i) { 824 // Because we are not doing a blocking drag and drop, the original 825 // OSDragExchangeData object is lost as soon as we return from the drag 826 // initiation in DragDropController::StartDragAndDrop(). Hence we set the 827 // drag_data_ to a fake drag data object that we created. 828 if (i > 0) 829 UpdateDragData(&data); 830 generator.MoveMouseBy(0, 1); 831 832 // Execute any scheduled draws to process deferred mouse events. 833 RunAllPendingInMessageLoop(); 834 } 835 // Make sure the capture window won't handle mouse events. 836 aura::Window* capture_window = drag_drop_tracker()->capture_window(); 837 ASSERT_TRUE(!!capture_window); 838 EXPECT_EQ("0x0", capture_window->bounds().size().ToString()); 839 EXPECT_EQ(NULL, 840 capture_window->GetEventHandlerForPoint(gfx::Point())); 841 EXPECT_EQ(NULL, 842 capture_window->GetTopWindowContainingPoint(gfx::Point())); 843 844 aura::client::GetCaptureClient(widget->GetNativeView()->GetRootWindow())-> 845 SetCapture(NULL); 846 847 EXPECT_TRUE(drag_drop_controller_->drag_start_received_); 848 EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(), 849 drag_drop_controller_->num_drag_updates_); 850 EXPECT_FALSE(drag_drop_controller_->drop_received_); 851 EXPECT_TRUE(drag_drop_controller_->drag_canceled_); 852 EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"), 853 drag_drop_controller_->drag_string_); 854 855 EXPECT_EQ(1, drag_view->num_drag_enters_); 856 EXPECT_EQ(num_drags - 1 - drag_view->VerticalDragThreshold(), 857 drag_view->num_drag_updates_); 858 EXPECT_EQ(0, drag_view->num_drops_); 859 EXPECT_EQ(1, drag_view->num_drag_exits_); 860 EXPECT_TRUE(drag_view->drag_done_received_); 861 } 862 863 TEST_F(DragDropControllerTest, TouchDragDropInMultipleWindows) { 864 CommandLine::ForCurrentProcess()->AppendSwitch( 865 switches::kEnableTouchDragDrop); 866 scoped_ptr<views::Widget> widget1(CreateNewWidget()); 867 DragTestView* drag_view1 = new DragTestView; 868 AddViewToWidgetAndResize(widget1.get(), drag_view1); 869 scoped_ptr<views::Widget> widget2(CreateNewWidget()); 870 DragTestView* drag_view2 = new DragTestView; 871 AddViewToWidgetAndResize(widget2.get(), drag_view2); 872 gfx::Rect widget1_bounds = widget1->GetClientAreaBoundsInScreen(); 873 gfx::Rect widget2_bounds = widget2->GetClientAreaBoundsInScreen(); 874 widget2->SetBounds(gfx::Rect(widget1_bounds.width(), 0, 875 widget2_bounds.width(), widget2_bounds.height())); 876 877 ui::OSExchangeData data; 878 data.SetString(base::UTF8ToUTF16("I am being dragged")); 879 880 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 881 widget1->GetNativeView()); 882 generator.PressTouch(); 883 gfx::Point point = gfx::Rect(drag_view1->bounds()).CenterPoint(); 884 DispatchGesture(ui::ET_GESTURE_LONG_PRESS, point); 885 // Because we are not doing a blocking drag and drop, the original 886 // OSDragExchangeData object is lost as soon as we return from the drag 887 // initiation in DragDropController::StartDragAndDrop(). Hence we set the 888 // drag_data_ to a fake drag data object that we created. 889 UpdateDragData(&data); 890 gfx::Point gesture_location = point; 891 int num_drags = drag_view1->width(); 892 for (int i = 0; i < num_drags; ++i) { 893 gesture_location.Offset(1, 0); 894 DispatchGesture(ui::ET_GESTURE_SCROLL_UPDATE, gesture_location); 895 896 // Execute any scheduled draws to process deferred mouse events. 897 RunAllPendingInMessageLoop(); 898 } 899 900 DispatchGesture(ui::ET_GESTURE_SCROLL_END, gesture_location); 901 902 EXPECT_TRUE(drag_drop_controller_->drag_start_received_); 903 EXPECT_EQ(num_drags, drag_drop_controller_->num_drag_updates_); 904 EXPECT_TRUE(drag_drop_controller_->drop_received_); 905 EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"), 906 drag_drop_controller_->drag_string_); 907 908 EXPECT_EQ(1, drag_view1->num_drag_enters_); 909 int num_expected_updates = drag_view1->bounds().width() - 910 drag_view1->bounds().CenterPoint().x() - 1; 911 EXPECT_EQ(num_expected_updates, drag_view1->num_drag_updates_); 912 EXPECT_EQ(0, drag_view1->num_drops_); 913 EXPECT_EQ(1, drag_view1->num_drag_exits_); 914 EXPECT_TRUE(drag_view1->drag_done_received_); 915 916 EXPECT_EQ(1, drag_view2->num_drag_enters_); 917 num_expected_updates = num_drags - num_expected_updates; 918 EXPECT_EQ(num_expected_updates, drag_view2->num_drag_updates_); 919 EXPECT_EQ(1, drag_view2->num_drops_); 920 EXPECT_EQ(0, drag_view2->num_drag_exits_); 921 EXPECT_FALSE(drag_view2->drag_done_received_); 922 } 923 924 TEST_F(DragDropControllerTest, TouchDragDropCancelsOnLongTap) { 925 CommandLine::ForCurrentProcess()->AppendSwitch( 926 switches::kEnableTouchDragDrop); 927 scoped_ptr<views::Widget> widget(CreateNewWidget()); 928 DragTestView* drag_view = new DragTestView; 929 AddViewToWidgetAndResize(widget.get(), drag_view); 930 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 931 widget->GetNativeView()); 932 933 generator.PressTouch(); 934 gfx::Point point = gfx::Rect(drag_view->bounds()).CenterPoint(); 935 DispatchGesture(ui::ET_GESTURE_LONG_PRESS, point); 936 DispatchGesture(ui::ET_GESTURE_LONG_TAP, point); 937 938 EXPECT_TRUE(drag_drop_controller_->drag_start_received_); 939 EXPECT_TRUE(drag_drop_controller_->drag_canceled_); 940 EXPECT_EQ(0, drag_drop_controller_->num_drag_updates_); 941 EXPECT_FALSE(drag_drop_controller_->drop_received_); 942 EXPECT_EQ(base::UTF8ToUTF16("I am being dragged"), 943 drag_drop_controller_->drag_string_); 944 EXPECT_EQ(0, drag_view->num_drag_enters_); 945 EXPECT_EQ(0, drag_view->num_drops_); 946 EXPECT_EQ(0, drag_view->num_drag_exits_); 947 EXPECT_TRUE(drag_view->drag_done_received_); 948 } 949 950 TEST_F(DragDropControllerTest, TouchDragDropLongTapGestureIsForwarded) { 951 CommandLine::ForCurrentProcess()->AppendSwitch( 952 switches::kEnableTouchDragDrop); 953 scoped_ptr<views::Widget> widget(CreateNewWidget()); 954 DragTestView* drag_view = new DragTestView; 955 AddViewToWidgetAndResize(widget.get(), drag_view); 956 aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), 957 widget->GetNativeView()); 958 959 generator.PressTouch(); 960 gfx::Point point = gfx::Rect(drag_view->bounds()).CenterPoint(); 961 DispatchGesture(ui::ET_GESTURE_LONG_PRESS, point); 962 963 // Since we are not running inside a nested loop, the |drag_source_window_| 964 // will get destroyed immediately. Hence we reassign it. 965 EXPECT_EQ(NULL, GetDragSourceWindow()); 966 SetDragSourceWindow(widget->GetNativeView()); 967 EXPECT_FALSE(drag_view->long_tap_received_); 968 DispatchGesture(ui::ET_GESTURE_LONG_TAP, point); 969 CompleteCancelAnimation(); 970 EXPECT_TRUE(drag_view->long_tap_received_); 971 } 972 973 namespace { 974 975 class DragImageWindowObserver : public aura::WindowObserver { 976 public: 977 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { 978 window_location_on_destroying_ = window->GetBoundsInScreen().origin(); 979 } 980 981 gfx::Point window_location_on_destroying() const { 982 return window_location_on_destroying_; 983 } 984 985 public: 986 gfx::Point window_location_on_destroying_; 987 }; 988 989 } 990 991 // Verifies the drag image moves back to the position where drag is started 992 // across displays when drag is cancelled. 993 TEST_F(DragDropControllerTest, DragCancelAcrossDisplays) { 994 if (!SupportsMultipleDisplays()) 995 return; 996 997 UpdateDisplay("400x400,400x400"); 998 aura::Window::Windows root_windows = 999 Shell::GetInstance()->GetAllRootWindows(); 1000 for (aura::Window::Windows::iterator iter = root_windows.begin(); 1001 iter != root_windows.end(); ++iter) { 1002 aura::client::SetDragDropClient(*iter, drag_drop_controller_.get()); 1003 } 1004 1005 ui::OSExchangeData data; 1006 data.SetString(base::UTF8ToUTF16("I am being dragged")); 1007 { 1008 scoped_ptr<views::Widget> widget(CreateNewWidget()); 1009 aura::Window* window = widget->GetNativeWindow(); 1010 drag_drop_controller_->StartDragAndDrop( 1011 data, 1012 window->GetRootWindow(), 1013 window, 1014 gfx::Point(5, 5), 1015 ui::DragDropTypes::DRAG_MOVE, 1016 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); 1017 1018 DragImageWindowObserver observer; 1019 ASSERT_TRUE(GetDragImageWindow()); 1020 GetDragImageWindow()->AddObserver(&observer); 1021 1022 { 1023 ui::MouseEvent e(ui::ET_MOUSE_DRAGGED, 1024 gfx::Point(200, 0), 1025 gfx::Point(200, 0), 1026 ui::EF_NONE, 1027 ui::EF_NONE); 1028 drag_drop_controller_->DragUpdate(window, e); 1029 } 1030 { 1031 ui::MouseEvent e(ui::ET_MOUSE_DRAGGED, 1032 gfx::Point(600, 0), 1033 gfx::Point(600, 0), 1034 ui::EF_NONE, 1035 ui::EF_NONE); 1036 drag_drop_controller_->DragUpdate(window, e); 1037 } 1038 1039 drag_drop_controller_->DragCancel(); 1040 CompleteCancelAnimation(); 1041 1042 EXPECT_EQ("5,5", observer.window_location_on_destroying().ToString()); 1043 } 1044 1045 { 1046 scoped_ptr<views::Widget> widget(CreateNewWidget()); 1047 aura::Window* window = widget->GetNativeWindow(); 1048 drag_drop_controller_->StartDragAndDrop( 1049 data, 1050 window->GetRootWindow(), 1051 window, 1052 gfx::Point(405, 405), 1053 ui::DragDropTypes::DRAG_MOVE, 1054 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); 1055 DragImageWindowObserver observer; 1056 ASSERT_TRUE(GetDragImageWindow()); 1057 GetDragImageWindow()->AddObserver(&observer); 1058 1059 { 1060 ui::MouseEvent e(ui::ET_MOUSE_DRAGGED, 1061 gfx::Point(600, 0), 1062 gfx::Point(600, 0), 1063 ui::EF_NONE, 1064 ui::EF_NONE); 1065 drag_drop_controller_->DragUpdate(window, e); 1066 } 1067 { 1068 ui::MouseEvent e(ui::ET_MOUSE_DRAGGED, 1069 gfx::Point(200, 0), 1070 gfx::Point(200, 0), 1071 ui::EF_NONE, 1072 ui::EF_NONE); 1073 drag_drop_controller_->DragUpdate(window, e); 1074 } 1075 1076 drag_drop_controller_->DragCancel(); 1077 CompleteCancelAnimation(); 1078 1079 EXPECT_EQ("405,405", observer.window_location_on_destroying().ToString()); 1080 } 1081 for (aura::Window::Windows::iterator iter = root_windows.begin(); 1082 iter != root_windows.end(); ++iter) { 1083 aura::client::SetDragDropClient(*iter, NULL); 1084 } 1085 } 1086 1087 } // namespace test 1088 } // namespace aura 1089