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