1 // Copyright (c) 2013 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/basictypes.h" 6 #include "base/bind.h" 7 #include "base/command_line.h" 8 #include "base/path_service.h" 9 #include "base/run_loop.h" 10 #include "base/strings/stringprintf.h" 11 #include "base/strings/utf_string_conversions.h" 12 #include "ui/aura/client/focus_client.h" 13 #include "ui/aura/env.h" 14 #include "ui/aura/test/event_generator.h" 15 #include "ui/aura/window.h" 16 #include "ui/aura/window_tree_host.h" 17 #include "ui/base/resource/resource_bundle.h" 18 #include "ui/base/ui_base_paths.h" 19 #include "ui/base/ui_base_switches.h" 20 #include "ui/events/event_processor.h" 21 #include "ui/gfx/native_widget_types.h" 22 #include "ui/gl/gl_surface.h" 23 #include "ui/views/controls/textfield/textfield.h" 24 #include "ui/views/controls/textfield/textfield_test_api.h" 25 #include "ui/views/test/widget_test.h" 26 #include "ui/views/touchui/touch_selection_controller_impl.h" 27 #include "ui/views/widget/widget.h" 28 #include "ui/views/window/dialog_delegate.h" 29 #include "ui/wm/public/activation_client.h" 30 31 #if !defined(OS_CHROMEOS) 32 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" 33 #endif 34 35 #if defined(OS_WIN) 36 #include "ui/views/win/hwnd_util.h" 37 #endif 38 39 namespace views { 40 namespace test { 41 42 namespace { 43 44 // A View that closes the Widget and exits the current message-loop when it 45 // receives a mouse-release event. 46 class ExitLoopOnRelease : public View { 47 public: 48 ExitLoopOnRelease() {} 49 virtual ~ExitLoopOnRelease() {} 50 51 private: 52 // Overridden from View: 53 virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE { 54 GetWidget()->Close(); 55 base::MessageLoop::current()->QuitNow(); 56 } 57 58 DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease); 59 }; 60 61 // A view that does a capture on gesture-begin events. 62 class GestureCaptureView : public View { 63 public: 64 GestureCaptureView() {} 65 virtual ~GestureCaptureView() {} 66 67 private: 68 // Overridden from View: 69 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 70 if (event->type() == ui::ET_GESTURE_BEGIN) { 71 GetWidget()->SetCapture(this); 72 event->StopPropagation(); 73 } 74 } 75 76 DISALLOW_COPY_AND_ASSIGN(GestureCaptureView); 77 }; 78 79 // A view that always processes all mouse events. 80 class MouseView : public View { 81 public: 82 MouseView() 83 : View(), 84 entered_(0), 85 exited_(0), 86 pressed_(0) { 87 } 88 virtual ~MouseView() {} 89 90 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { 91 pressed_++; 92 return true; 93 } 94 95 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE { 96 entered_++; 97 } 98 99 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE { 100 exited_++; 101 } 102 103 // Return the number of OnMouseEntered calls and reset the counter. 104 int EnteredCalls() { 105 int i = entered_; 106 entered_ = 0; 107 return i; 108 } 109 110 // Return the number of OnMouseExited calls and reset the counter. 111 int ExitedCalls() { 112 int i = exited_; 113 exited_ = 0; 114 return i; 115 } 116 117 int pressed() const { return pressed_; } 118 119 private: 120 int entered_; 121 int exited_; 122 123 int pressed_; 124 125 DISALLOW_COPY_AND_ASSIGN(MouseView); 126 }; 127 128 // A View that shows a different widget, sets capture on that widget, and 129 // initiates a nested message-loop when it receives a mouse-press event. 130 class NestedLoopCaptureView : public View { 131 public: 132 explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {} 133 virtual ~NestedLoopCaptureView() {} 134 135 private: 136 // Overridden from View: 137 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { 138 // Start a nested loop. 139 widget_->Show(); 140 widget_->SetCapture(widget_->GetContentsView()); 141 EXPECT_TRUE(widget_->HasCapture()); 142 143 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); 144 base::MessageLoop::ScopedNestableTaskAllower allow(loop); 145 146 base::RunLoop run_loop; 147 run_loop.Run(); 148 return true; 149 } 150 151 Widget* widget_; 152 153 DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView); 154 }; 155 156 } // namespace 157 158 class WidgetTestInteractive : public WidgetTest { 159 public: 160 WidgetTestInteractive() {} 161 virtual ~WidgetTestInteractive() {} 162 163 virtual void SetUp() OVERRIDE { 164 gfx::GLSurface::InitializeOneOffForTests(); 165 base::FilePath pak_dir; 166 PathService::Get(base::DIR_MODULE, &pak_dir); 167 base::FilePath pak_file; 168 pak_file = pak_dir.Append(FILE_PATH_LITERAL("ui_test.pak")); 169 ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file); 170 WidgetTest::SetUp(); 171 } 172 173 protected: 174 static void ShowQuickMenuImmediately( 175 TouchSelectionControllerImpl* controller) { 176 DCHECK(controller); 177 if (controller->context_menu_timer_.IsRunning()) { 178 controller->context_menu_timer_.Stop(); 179 controller->ContextMenuTimerFired(); 180 } 181 } 182 183 static bool IsQuickMenuVisible(TouchSelectionControllerImpl* controller) { 184 DCHECK(controller); 185 return controller->context_menu_ && controller->context_menu_->visible(); 186 } 187 }; 188 189 #if defined(OS_WIN) 190 // Tests whether activation and focus change works correctly in Windows. 191 // We test the following:- 192 // 1. If the active aura window is correctly set when a top level widget is 193 // created. 194 // 2. If the active aura window in widget 1 created above, is set to NULL when 195 // another top level widget is created and focused. 196 // 3. On focusing the native platform window for widget 1, the active aura 197 // window for widget 1 should be set and that for widget 2 should reset. 198 // TODO(ananta): Discuss with erg on how to write this test for linux x11 aura. 199 TEST_F(WidgetTestInteractive, DesktopNativeWidgetAuraActivationAndFocusTest) { 200 // Create widget 1 and expect the active window to be its window. 201 View* contents_view1 = new View; 202 contents_view1->SetFocusable(true); 203 Widget widget1; 204 Widget::InitParams init_params = 205 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 206 init_params.bounds = gfx::Rect(0, 0, 200, 200); 207 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 208 init_params.native_widget = new DesktopNativeWidgetAura(&widget1); 209 widget1.Init(init_params); 210 widget1.SetContentsView(contents_view1); 211 widget1.Show(); 212 aura::Window* root_window1= widget1.GetNativeView()->GetRootWindow(); 213 contents_view1->RequestFocus(); 214 215 EXPECT_TRUE(root_window1 != NULL); 216 aura::client::ActivationClient* activation_client1 = 217 aura::client::GetActivationClient(root_window1); 218 EXPECT_TRUE(activation_client1 != NULL); 219 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView()); 220 221 // Create widget 2 and expect the active window to be its window. 222 View* contents_view2 = new View; 223 Widget widget2; 224 Widget::InitParams init_params2 = 225 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 226 init_params2.bounds = gfx::Rect(0, 0, 200, 200); 227 init_params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 228 init_params2.native_widget = new DesktopNativeWidgetAura(&widget2); 229 widget2.Init(init_params2); 230 widget2.SetContentsView(contents_view2); 231 widget2.Show(); 232 aura::Window* root_window2 = widget2.GetNativeView()->GetRootWindow(); 233 contents_view2->RequestFocus(); 234 ::SetActiveWindow( 235 root_window2->GetHost()->GetAcceleratedWidget()); 236 237 aura::client::ActivationClient* activation_client2 = 238 aura::client::GetActivationClient(root_window2); 239 EXPECT_TRUE(activation_client2 != NULL); 240 EXPECT_EQ(activation_client2->GetActiveWindow(), widget2.GetNativeView()); 241 EXPECT_EQ(activation_client1->GetActiveWindow(), 242 reinterpret_cast<aura::Window*>(NULL)); 243 244 // Now set focus back to widget 1 and expect the active window to be its 245 // window. 246 contents_view1->RequestFocus(); 247 ::SetActiveWindow( 248 root_window1->GetHost()->GetAcceleratedWidget()); 249 EXPECT_EQ(activation_client2->GetActiveWindow(), 250 reinterpret_cast<aura::Window*>(NULL)); 251 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView()); 252 } 253 #endif 254 255 TEST_F(WidgetTestInteractive, CaptureAutoReset) { 256 Widget* toplevel = CreateTopLevelFramelessPlatformWidget(); 257 View* container = new View; 258 toplevel->SetContentsView(container); 259 260 EXPECT_FALSE(toplevel->HasCapture()); 261 toplevel->SetCapture(NULL); 262 EXPECT_TRUE(toplevel->HasCapture()); 263 264 // By default, mouse release removes capture. 265 gfx::Point click_location(45, 15); 266 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location, 267 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 268 toplevel->OnMouseEvent(&release); 269 EXPECT_FALSE(toplevel->HasCapture()); 270 271 // Now a mouse release shouldn't remove capture. 272 toplevel->set_auto_release_capture(false); 273 toplevel->SetCapture(NULL); 274 EXPECT_TRUE(toplevel->HasCapture()); 275 toplevel->OnMouseEvent(&release); 276 EXPECT_TRUE(toplevel->HasCapture()); 277 toplevel->ReleaseCapture(); 278 EXPECT_FALSE(toplevel->HasCapture()); 279 280 toplevel->Close(); 281 RunPendingMessages(); 282 } 283 284 TEST_F(WidgetTestInteractive, ResetCaptureOnGestureEnd) { 285 Widget* toplevel = CreateTopLevelFramelessPlatformWidget(); 286 View* container = new View; 287 toplevel->SetContentsView(container); 288 289 View* gesture = new GestureCaptureView; 290 gesture->SetBounds(0, 0, 30, 30); 291 container->AddChildView(gesture); 292 293 MouseView* mouse = new MouseView; 294 mouse->SetBounds(30, 0, 30, 30); 295 container->AddChildView(mouse); 296 297 toplevel->SetSize(gfx::Size(100, 100)); 298 toplevel->Show(); 299 300 // Start a gesture on |gesture|. 301 ui::GestureEvent begin(ui::ET_GESTURE_BEGIN, 302 15, 15, 0, base::TimeDelta(), 303 ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1); 304 ui::GestureEvent end(ui::ET_GESTURE_END, 305 15, 15, 0, base::TimeDelta(), 306 ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1); 307 toplevel->OnGestureEvent(&begin); 308 309 // Now try to click on |mouse|. Since |gesture| will have capture, |mouse| 310 // will not receive the event. 311 gfx::Point click_location(45, 15); 312 313 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location, 314 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 315 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location, 316 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 317 318 EXPECT_TRUE(toplevel->HasCapture()); 319 320 toplevel->OnMouseEvent(&press); 321 toplevel->OnMouseEvent(&release); 322 EXPECT_EQ(0, mouse->pressed()); 323 324 EXPECT_FALSE(toplevel->HasCapture()); 325 326 // The end of the gesture should release the capture, and pressing on |mouse| 327 // should now reach |mouse|. 328 toplevel->OnGestureEvent(&end); 329 toplevel->OnMouseEvent(&press); 330 toplevel->OnMouseEvent(&release); 331 EXPECT_EQ(1, mouse->pressed()); 332 333 toplevel->Close(); 334 RunPendingMessages(); 335 } 336 337 // Checks that if a mouse-press triggers a capture on a different widget (which 338 // consumes the mouse-release event), then the target of the press does not have 339 // capture. 340 TEST_F(WidgetTestInteractive, DisableCaptureWidgetFromMousePress) { 341 // The test creates two widgets: |first| and |second|. 342 // The View in |first| makes |second| visible, sets capture on it, and starts 343 // a nested loop (like a menu does). The View in |second| terminates the 344 // nested loop and closes the widget. 345 // The test sends a mouse-press event to |first|, and posts a task to send a 346 // release event to |second|, to make sure that the release event is 347 // dispatched after the nested loop starts. 348 349 Widget* first = CreateTopLevelFramelessPlatformWidget(); 350 Widget* second = CreateTopLevelFramelessPlatformWidget(); 351 352 View* container = new NestedLoopCaptureView(second); 353 first->SetContentsView(container); 354 355 second->SetContentsView(new ExitLoopOnRelease()); 356 357 first->SetSize(gfx::Size(100, 100)); 358 first->Show(); 359 360 gfx::Point location(20, 20); 361 base::MessageLoop::current()->PostTask(FROM_HERE, 362 base::Bind(&Widget::OnMouseEvent, 363 base::Unretained(second), 364 base::Owned(new ui::MouseEvent(ui::ET_MOUSE_RELEASED, 365 location, 366 location, 367 ui::EF_LEFT_MOUSE_BUTTON, 368 ui::EF_LEFT_MOUSE_BUTTON)))); 369 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location, 370 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 371 first->OnMouseEvent(&press); 372 EXPECT_FALSE(first->HasCapture()); 373 first->Close(); 374 RunPendingMessages(); 375 } 376 377 // Tests some grab/ungrab events. 378 // TODO(estade): can this be enabled now that this is an interactive ui test? 379 TEST_F(WidgetTestInteractive, DISABLED_GrabUngrab) { 380 Widget* toplevel = CreateTopLevelPlatformWidget(); 381 Widget* child1 = CreateChildNativeWidgetWithParent(toplevel); 382 Widget* child2 = CreateChildNativeWidgetWithParent(toplevel); 383 384 toplevel->SetBounds(gfx::Rect(0, 0, 500, 500)); 385 386 child1->SetBounds(gfx::Rect(10, 10, 300, 300)); 387 View* view = new MouseView(); 388 view->SetBounds(0, 0, 300, 300); 389 child1->GetRootView()->AddChildView(view); 390 391 child2->SetBounds(gfx::Rect(200, 10, 200, 200)); 392 view = new MouseView(); 393 view->SetBounds(0, 0, 200, 200); 394 child2->GetRootView()->AddChildView(view); 395 396 toplevel->Show(); 397 RunPendingMessages(); 398 399 // Click on child1 400 gfx::Point p1(45, 45); 401 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, 402 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 403 toplevel->OnMouseEvent(&pressed); 404 405 EXPECT_TRUE(toplevel->HasCapture()); 406 EXPECT_TRUE(child1->HasCapture()); 407 EXPECT_FALSE(child2->HasCapture()); 408 409 ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1, 410 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 411 toplevel->OnMouseEvent(&released); 412 413 EXPECT_FALSE(toplevel->HasCapture()); 414 EXPECT_FALSE(child1->HasCapture()); 415 EXPECT_FALSE(child2->HasCapture()); 416 417 RunPendingMessages(); 418 419 // Click on child2 420 gfx::Point p2(315, 45); 421 ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2, 422 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 423 toplevel->OnMouseEvent(&pressed2); 424 EXPECT_TRUE(pressed2.handled()); 425 EXPECT_TRUE(toplevel->HasCapture()); 426 EXPECT_TRUE(child2->HasCapture()); 427 EXPECT_FALSE(child1->HasCapture()); 428 429 ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2, 430 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); 431 toplevel->OnMouseEvent(&released2); 432 EXPECT_FALSE(toplevel->HasCapture()); 433 EXPECT_FALSE(child1->HasCapture()); 434 EXPECT_FALSE(child2->HasCapture()); 435 436 toplevel->CloseNow(); 437 } 438 439 // Tests mouse move outside of the window into the "resize controller" and back 440 // will still generate an OnMouseEntered and OnMouseExited event.. 441 TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) { 442 Widget* toplevel = CreateTopLevelPlatformWidget(); 443 444 toplevel->SetBounds(gfx::Rect(0, 0, 100, 100)); 445 446 MouseView* view = new MouseView(); 447 view->SetBounds(90, 90, 10, 10); 448 toplevel->GetRootView()->AddChildView(view); 449 450 toplevel->Show(); 451 RunPendingMessages(); 452 453 // Move to an outside position. 454 gfx::Point p1(200, 200); 455 ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EF_NONE, 456 ui::EF_NONE); 457 toplevel->OnMouseEvent(&moved_out); 458 EXPECT_EQ(0, view->EnteredCalls()); 459 EXPECT_EQ(0, view->ExitedCalls()); 460 461 // Move onto the active view. 462 gfx::Point p2(95, 95); 463 ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EF_NONE, 464 ui::EF_NONE); 465 toplevel->OnMouseEvent(&moved_over); 466 EXPECT_EQ(1, view->EnteredCalls()); 467 EXPECT_EQ(0, view->ExitedCalls()); 468 469 // Move onto the outer resizing border. 470 gfx::Point p3(102, 95); 471 ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3, ui::EF_NONE, 472 ui::EF_NONE); 473 toplevel->OnMouseEvent(&moved_resizer); 474 EXPECT_EQ(0, view->EnteredCalls()); 475 EXPECT_EQ(1, view->ExitedCalls()); 476 477 // Move onto the view again. 478 toplevel->OnMouseEvent(&moved_over); 479 EXPECT_EQ(1, view->EnteredCalls()); 480 EXPECT_EQ(0, view->ExitedCalls()); 481 482 RunPendingMessages(); 483 484 toplevel->CloseNow(); 485 } 486 487 // Test view focus restoration when a widget is deactivated and re-activated. 488 TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) { 489 Widget* widget1 = CreateTopLevelPlatformWidget(); 490 View* view1 = new View; 491 view1->SetFocusable(true); 492 widget1->GetContentsView()->AddChildView(view1); 493 494 Widget* widget2 = CreateTopLevelPlatformWidget(); 495 View* view2a = new View; 496 View* view2b = new View; 497 view2a->SetFocusable(true); 498 view2b->SetFocusable(true); 499 widget2->GetContentsView()->AddChildView(view2a); 500 widget2->GetContentsView()->AddChildView(view2b); 501 502 widget1->Show(); 503 EXPECT_TRUE(widget1->IsActive()); 504 view1->RequestFocus(); 505 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView()); 506 507 widget2->Show(); 508 EXPECT_TRUE(widget2->IsActive()); 509 EXPECT_FALSE(widget1->IsActive()); 510 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView()); 511 view2a->RequestFocus(); 512 EXPECT_EQ(view2a, widget2->GetFocusManager()->GetFocusedView()); 513 view2b->RequestFocus(); 514 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView()); 515 516 widget1->Activate(); 517 EXPECT_TRUE(widget1->IsActive()); 518 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView()); 519 EXPECT_FALSE(widget2->IsActive()); 520 EXPECT_EQ(NULL, widget2->GetFocusManager()->GetFocusedView()); 521 522 widget2->Activate(); 523 EXPECT_TRUE(widget2->IsActive()); 524 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView()); 525 EXPECT_FALSE(widget1->IsActive()); 526 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView()); 527 528 widget1->CloseNow(); 529 widget2->CloseNow(); 530 } 531 532 #if defined(OS_WIN) 533 534 // Test view focus retention when a widget's HWND is disabled and re-enabled. 535 TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) { 536 Widget* widget = CreateTopLevelFramelessPlatformWidget(); 537 widget->SetContentsView(new View); 538 for (size_t i = 0; i < 2; ++i) { 539 widget->GetContentsView()->AddChildView(new View); 540 widget->GetContentsView()->child_at(i)->SetFocusable(true); 541 } 542 543 widget->Show(); 544 const HWND hwnd = HWNDForWidget(widget); 545 EXPECT_TRUE(::IsWindow(hwnd)); 546 EXPECT_TRUE(::IsWindowEnabled(hwnd)); 547 EXPECT_EQ(hwnd, ::GetActiveWindow()); 548 549 for (int i = 0; i < widget->GetContentsView()->child_count(); ++i) { 550 SCOPED_TRACE(base::StringPrintf("Child view %d", i)); 551 View* view = widget->GetContentsView()->child_at(i); 552 553 view->RequestFocus(); 554 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView()); 555 EXPECT_FALSE(::EnableWindow(hwnd, FALSE)); 556 EXPECT_FALSE(::IsWindowEnabled(hwnd)); 557 558 // Oddly, disabling the HWND leaves it active with the focus unchanged. 559 EXPECT_EQ(hwnd, ::GetActiveWindow()); 560 EXPECT_TRUE(widget->IsActive()); 561 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView()); 562 563 EXPECT_TRUE(::EnableWindow(hwnd, TRUE)); 564 EXPECT_TRUE(::IsWindowEnabled(hwnd)); 565 EXPECT_EQ(hwnd, ::GetActiveWindow()); 566 EXPECT_TRUE(widget->IsActive()); 567 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView()); 568 } 569 570 widget->CloseNow(); 571 } 572 573 // This class subclasses the Widget class to listen for activation change 574 // notifications and provides accessors to return information as to whether 575 // the widget is active. We need this to ensure that users of the widget 576 // class activate the widget only when the underlying window becomes really 577 // active. Previously we would activate the widget in the WM_NCACTIVATE 578 // message which is incorrect because APIs like FlashWindowEx flash the 579 // window caption by sending fake WM_NCACTIVATE messages. 580 class WidgetActivationTest : public Widget { 581 public: 582 WidgetActivationTest() 583 : active_(false) {} 584 585 virtual ~WidgetActivationTest() {} 586 587 virtual void OnNativeWidgetActivationChanged(bool active) OVERRIDE { 588 active_ = active; 589 } 590 591 bool active() const { return active_; } 592 593 private: 594 bool active_; 595 596 DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest); 597 }; 598 599 // Tests whether the widget only becomes active when the underlying window 600 // is really active. 601 TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) { 602 WidgetActivationTest widget1; 603 Widget::InitParams init_params = 604 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 605 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 606 init_params.native_widget = new DesktopNativeWidgetAura(&widget1); 607 init_params.bounds = gfx::Rect(0, 0, 200, 200); 608 widget1.Init(init_params); 609 widget1.Show(); 610 EXPECT_EQ(true, widget1.active()); 611 612 WidgetActivationTest widget2; 613 init_params.native_widget = new DesktopNativeWidgetAura(&widget2); 614 widget2.Init(init_params); 615 widget2.Show(); 616 EXPECT_EQ(true, widget2.active()); 617 EXPECT_EQ(false, widget1.active()); 618 619 HWND win32_native_window1 = HWNDForWidget(&widget1); 620 EXPECT_TRUE(::IsWindow(win32_native_window1)); 621 622 ::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0); 623 EXPECT_EQ(false, widget1.active()); 624 EXPECT_EQ(true, widget2.active()); 625 626 ::SetActiveWindow(win32_native_window1); 627 EXPECT_EQ(true, widget1.active()); 628 EXPECT_EQ(false, widget2.active()); 629 } 630 #endif 631 632 #if !defined(OS_CHROMEOS) 633 // Provides functionality to create a window modal dialog. 634 class ModalDialogDelegate : public DialogDelegateView { 635 public: 636 explicit ModalDialogDelegate(ui::ModalType type) : type_(type) {} 637 virtual ~ModalDialogDelegate() {} 638 639 // WidgetDelegate overrides. 640 virtual ui::ModalType GetModalType() const OVERRIDE { 641 return type_; 642 } 643 644 private: 645 ui::ModalType type_; 646 647 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate); 648 }; 649 650 // Tests whether the focused window is set correctly when a modal window is 651 // created and destroyed. When it is destroyed it should focus the owner window. 652 TEST_F(WidgetTestInteractive, WindowModalWindowDestroyedActivationTest) { 653 // Create a top level widget. 654 Widget top_level_widget; 655 Widget::InitParams init_params = 656 CreateParams(Widget::InitParams::TYPE_WINDOW); 657 init_params.show_state = ui::SHOW_STATE_NORMAL; 658 gfx::Rect initial_bounds(0, 0, 500, 500); 659 init_params.bounds = initial_bounds; 660 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 661 init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget); 662 top_level_widget.Init(init_params); 663 top_level_widget.Show(); 664 665 aura::Window* top_level_window = top_level_widget.GetNativeWindow(); 666 EXPECT_EQ(top_level_window, aura::client::GetFocusClient( 667 top_level_window)->GetFocusedWindow()); 668 669 // Create a modal dialog. 670 // This instance will be destroyed when the dialog is destroyed. 671 ModalDialogDelegate* dialog_delegate = 672 new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW); 673 674 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( 675 dialog_delegate, NULL, top_level_widget.GetNativeWindow()); 676 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); 677 modal_dialog_widget->Show(); 678 aura::Window* dialog_window = modal_dialog_widget->GetNativeWindow(); 679 EXPECT_EQ(dialog_window, aura::client::GetFocusClient( 680 top_level_window)->GetFocusedWindow()); 681 682 modal_dialog_widget->CloseNow(); 683 EXPECT_EQ(top_level_window, aura::client::GetFocusClient( 684 top_level_window)->GetFocusedWindow()); 685 top_level_widget.CloseNow(); 686 } 687 688 // Test that when opening a system-modal window, capture is released. 689 TEST_F(WidgetTestInteractive, SystemModalWindowReleasesCapture) { 690 // Create a top level widget. 691 Widget top_level_widget; 692 Widget::InitParams init_params = 693 CreateParams(Widget::InitParams::TYPE_WINDOW); 694 init_params.show_state = ui::SHOW_STATE_NORMAL; 695 gfx::Rect initial_bounds(0, 0, 500, 500); 696 init_params.bounds = initial_bounds; 697 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 698 init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget); 699 top_level_widget.Init(init_params); 700 top_level_widget.Show(); 701 702 aura::Window* top_level_window = top_level_widget.GetNativeWindow(); 703 EXPECT_EQ(top_level_window, aura::client::GetFocusClient( 704 top_level_window)->GetFocusedWindow()); 705 706 EXPECT_FALSE(top_level_window->HasCapture()); 707 top_level_window->SetCapture(); 708 EXPECT_TRUE(top_level_window->HasCapture()); 709 710 // Create a modal dialog. 711 ModalDialogDelegate* dialog_delegate = 712 new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM); 713 714 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( 715 dialog_delegate, NULL, top_level_widget.GetNativeWindow()); 716 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200)); 717 modal_dialog_widget->Show(); 718 719 EXPECT_FALSE(top_level_window->HasCapture()); 720 721 modal_dialog_widget->CloseNow(); 722 top_level_widget.CloseNow(); 723 } 724 725 #endif 726 727 TEST_F(WidgetTestInteractive, CanActivateFlagIsHonored) { 728 Widget widget; 729 Widget::InitParams init_params = 730 CreateParams(Widget::InitParams::TYPE_WINDOW); 731 init_params.bounds = gfx::Rect(0, 0, 200, 200); 732 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 733 init_params.activatable = Widget::InitParams::ACTIVATABLE_NO; 734 #if !defined(OS_CHROMEOS) 735 init_params.native_widget = new DesktopNativeWidgetAura(&widget); 736 #endif // !defined(OS_CHROMEOS) 737 widget.Init(init_params); 738 739 widget.Show(); 740 EXPECT_FALSE(widget.IsActive()); 741 } 742 743 // Test that touch selection quick menu is not activated when opened. 744 TEST_F(WidgetTestInteractive, TouchSelectionQuickMenuIsNotActivated) { 745 CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnableTouchEditing); 746 #if defined(OS_WIN) 747 views_delegate().set_use_desktop_native_widgets(true); 748 #endif // !defined(OS_WIN) 749 750 Widget widget; 751 Widget::InitParams init_params = 752 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); 753 init_params.bounds = gfx::Rect(0, 0, 200, 200); 754 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 755 widget.Init(init_params); 756 757 Textfield* textfield = new Textfield; 758 textfield->SetBounds(0, 0, 200, 20); 759 textfield->SetText(base::ASCIIToUTF16("some text")); 760 widget.GetRootView()->AddChildView(textfield); 761 762 widget.Show(); 763 textfield->RequestFocus(); 764 textfield->SelectAll(true); 765 TextfieldTestApi textfield_test_api(textfield); 766 767 RunPendingMessages(); 768 769 aura::test::EventGenerator generator(widget.GetNativeView()->GetRootWindow()); 770 generator.GestureTapAt(gfx::Point(10, 10)); 771 ShowQuickMenuImmediately(static_cast<TouchSelectionControllerImpl*>( 772 textfield_test_api.touch_selection_controller())); 773 774 EXPECT_TRUE(textfield->HasFocus()); 775 EXPECT_TRUE(widget.IsActive()); 776 EXPECT_TRUE(IsQuickMenuVisible(static_cast<TouchSelectionControllerImpl*>( 777 textfield_test_api.touch_selection_controller()))); 778 } 779 780 namespace { 781 782 // Used to veirfy OnMouseCaptureLost() has been invoked. 783 class CaptureLostTrackingWidget : public Widget { 784 public: 785 CaptureLostTrackingWidget() : got_capture_lost_(false) {} 786 virtual ~CaptureLostTrackingWidget() {} 787 788 bool GetAndClearGotCaptureLost() { 789 bool value = got_capture_lost_; 790 got_capture_lost_ = false; 791 return value; 792 } 793 794 // Widget: 795 virtual void OnMouseCaptureLost() OVERRIDE { 796 got_capture_lost_ = true; 797 Widget::OnMouseCaptureLost(); 798 } 799 800 private: 801 bool got_capture_lost_; 802 803 DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget); 804 }; 805 806 } // namespace 807 808 class WidgetCaptureTest : public ViewsTestBase { 809 public: 810 WidgetCaptureTest() { 811 } 812 813 virtual ~WidgetCaptureTest() { 814 } 815 816 virtual void SetUp() OVERRIDE { 817 gfx::GLSurface::InitializeOneOffForTests(); 818 base::FilePath pak_dir; 819 PathService::Get(base::DIR_MODULE, &pak_dir); 820 base::FilePath pak_file; 821 pak_file = pak_dir.Append(FILE_PATH_LITERAL("ui_test.pak")); 822 ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file); 823 ViewsTestBase::SetUp(); 824 } 825 826 // Verifies Widget::SetCapture() results in updating native capture along with 827 // invoking the right Widget function. 828 void TestCapture(bool use_desktop_native_widget) { 829 CaptureLostTrackingWidget widget1; 830 Widget::InitParams params1 = 831 CreateParams(views::Widget::InitParams::TYPE_WINDOW); 832 params1.native_widget = CreateNativeWidget(use_desktop_native_widget, 833 &widget1); 834 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 835 widget1.Init(params1); 836 widget1.Show(); 837 838 CaptureLostTrackingWidget widget2; 839 Widget::InitParams params2 = 840 CreateParams(views::Widget::InitParams::TYPE_WINDOW); 841 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 842 params2.native_widget = CreateNativeWidget(use_desktop_native_widget, 843 &widget2); 844 widget2.Init(params2); 845 widget2.Show(); 846 847 // Set capture to widget2 and verity it gets it. 848 widget2.SetCapture(widget2.GetRootView()); 849 EXPECT_FALSE(widget1.HasCapture()); 850 EXPECT_TRUE(widget2.HasCapture()); 851 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost()); 852 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost()); 853 854 // Set capture to widget1 and verify it gets it. 855 widget1.SetCapture(widget1.GetRootView()); 856 EXPECT_TRUE(widget1.HasCapture()); 857 EXPECT_FALSE(widget2.HasCapture()); 858 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost()); 859 EXPECT_TRUE(widget2.GetAndClearGotCaptureLost()); 860 861 // Release and verify no one has it. 862 widget1.ReleaseCapture(); 863 EXPECT_FALSE(widget1.HasCapture()); 864 EXPECT_FALSE(widget2.HasCapture()); 865 EXPECT_TRUE(widget1.GetAndClearGotCaptureLost()); 866 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost()); 867 } 868 869 private: 870 NativeWidget* CreateNativeWidget(bool create_desktop_native_widget, 871 Widget* widget) { 872 #if !defined(OS_CHROMEOS) 873 if (create_desktop_native_widget) 874 return new DesktopNativeWidgetAura(widget); 875 #endif 876 return NULL; 877 } 878 879 DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest); 880 }; 881 882 // See description in TestCapture(). 883 TEST_F(WidgetCaptureTest, Capture) { 884 TestCapture(false); 885 } 886 887 #if !defined(OS_LINUX) 888 // See description in TestCapture(). Creates DesktopNativeWidget. 889 TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) { 890 TestCapture(true); 891 } 892 #endif 893 894 #if !defined(OS_CHROMEOS) 895 namespace { 896 897 // Used to veirfy OnMouseEvent() has been invoked. 898 class MouseEventTrackingWidget : public Widget { 899 public: 900 MouseEventTrackingWidget() : got_mouse_event_(false) {} 901 virtual ~MouseEventTrackingWidget() {} 902 903 bool GetAndClearGotMouseEvent() { 904 bool value = got_mouse_event_; 905 got_mouse_event_ = false; 906 return value; 907 } 908 909 // Widget: 910 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { 911 got_mouse_event_ = true; 912 Widget::OnMouseEvent(event); 913 } 914 915 private: 916 bool got_mouse_event_; 917 918 DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget); 919 }; 920 921 } // namespace 922 923 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) 924 // TODO(erg): linux_aura bringup: http://crbug.com/163931 925 #define MAYBE_MouseEventDispatchedToRightWindow \ 926 DISABLED_MouseEventDispatchedToRightWindow 927 #else 928 #define MAYBE_MouseEventDispatchedToRightWindow \ 929 MouseEventDispatchedToRightWindow 930 #endif 931 932 // Verifies if a mouse event is received on a widget that doesn't have capture 933 // it is correctly processed by the widget that doesn't have capture. 934 TEST_F(WidgetCaptureTest, MAYBE_MouseEventDispatchedToRightWindow) { 935 MouseEventTrackingWidget widget1; 936 Widget::InitParams params1 = 937 CreateParams(views::Widget::InitParams::TYPE_WINDOW); 938 params1.native_widget = new DesktopNativeWidgetAura(&widget1); 939 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 940 widget1.Init(params1); 941 widget1.Show(); 942 943 MouseEventTrackingWidget widget2; 944 Widget::InitParams params2 = 945 CreateParams(views::Widget::InitParams::TYPE_WINDOW); 946 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 947 params2.native_widget = new DesktopNativeWidgetAura(&widget2); 948 widget2.Init(params2); 949 widget2.Show(); 950 951 // Set capture to widget2 and verity it gets it. 952 widget2.SetCapture(widget2.GetRootView()); 953 EXPECT_FALSE(widget1.HasCapture()); 954 EXPECT_TRUE(widget2.HasCapture()); 955 956 widget1.GetAndClearGotMouseEvent(); 957 widget2.GetAndClearGotMouseEvent(); 958 // Send a mouse event to the RootWindow associated with |widget1|. Even though 959 // |widget2| has capture, |widget1| should still get the event. 960 ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(), 961 ui::EF_NONE, ui::EF_NONE); 962 ui::EventDispatchDetails details = widget1.GetNativeWindow()-> 963 GetHost()->event_processor()->OnEventFromSource(&mouse_event); 964 ASSERT_FALSE(details.dispatcher_destroyed); 965 EXPECT_TRUE(widget1.GetAndClearGotMouseEvent()); 966 EXPECT_FALSE(widget2.GetAndClearGotMouseEvent()); 967 } 968 #endif 969 970 } // namespace test 971 } // namespace views 972