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.
5 #include "base/basictypes.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/event_utils.h"
17 #include "ui/events/test/event_generator.h"
18 #include "ui/gfx/native_widget_types.h"
19 #include "ui/gl/gl_surface.h"
20 #include "ui/views/controls/textfield/textfield.h"
21 #include "ui/views/controls/textfield/textfield_test_api.h"
22 #include "ui/views/focus/focus_manager.h"
23 #include "ui/views/test/focus_manager_test.h"
24 #include "ui/views/test/widget_test.h"
25 #include "ui/views/touchui/touch_selection_controller_impl.h"
26 #include "ui/views/widget/widget.h"
27 #include "ui/views/window/dialog_delegate.h"
28 #include "ui/wm/public/activation_client.h"
31 #include "ui/aura/window.h"
32 #include "ui/aura/window_tree_host.h"
33 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
34 #include "ui/views/win/hwnd_util.h"
42 // A View that closes the Widget and exits the current message-loop when it
43 // receives a mouse-release event.
44 class ExitLoopOnRelease
: public View
{
46 ExitLoopOnRelease() {}
47 ~ExitLoopOnRelease() override
{}
50 // Overridden from View:
51 void OnMouseReleased(const ui::MouseEvent
& event
) override
{
53 base::MessageLoop::current()->QuitNow();
56 DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease
);
59 // A view that does a capture on ui::ET_GESTURE_TAP_DOWN events.
60 class GestureCaptureView
: public View
{
62 GestureCaptureView() {}
63 ~GestureCaptureView() override
{}
66 // Overridden from View:
67 void OnGestureEvent(ui::GestureEvent
* event
) override
{
68 if (event
->type() == ui::ET_GESTURE_TAP_DOWN
) {
69 GetWidget()->SetCapture(this);
70 event
->StopPropagation();
74 DISALLOW_COPY_AND_ASSIGN(GestureCaptureView
);
77 // A view that always processes all mouse events.
78 class MouseView
: public View
{
86 ~MouseView() override
{}
88 bool OnMousePressed(const ui::MouseEvent
& event
) override
{
93 void OnMouseEntered(const ui::MouseEvent
& event
) override
{ entered_
++; }
95 void OnMouseExited(const ui::MouseEvent
& event
) override
{ exited_
++; }
97 // Return the number of OnMouseEntered calls and reset the counter.
104 // Return the number of OnMouseExited calls and reset the counter.
111 int pressed() const { return pressed_
; }
119 DISALLOW_COPY_AND_ASSIGN(MouseView
);
122 // A View that shows a different widget, sets capture on that widget, and
123 // initiates a nested message-loop when it receives a mouse-press event.
124 class NestedLoopCaptureView
: public View
{
126 explicit NestedLoopCaptureView(Widget
* widget
) : widget_(widget
) {}
127 ~NestedLoopCaptureView() override
{}
130 // Overridden from View:
131 bool OnMousePressed(const ui::MouseEvent
& event
) override
{
132 // Start a nested loop.
134 widget_
->SetCapture(widget_
->GetContentsView());
135 EXPECT_TRUE(widget_
->HasCapture());
137 base::MessageLoopForUI
* loop
= base::MessageLoopForUI::current();
138 base::MessageLoop::ScopedNestableTaskAllower
allow(loop
);
140 base::RunLoop run_loop
;
147 DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView
);
150 // Spins a run loop until a Widget's active state matches a desired state.
151 class WidgetActivationWaiter
: public WidgetObserver
{
153 WidgetActivationWaiter(Widget
* widget
, bool active
) : observed_(false) {
154 // Always expect a change for tests using this.
155 EXPECT_NE(active
, widget
->IsActive());
156 widget
->AddObserver(this);
164 void OnWidgetActivationChanged(Widget
* widget
, bool active
) override
{
166 widget
->RemoveObserver(this);
167 if (run_loop_
.running())
172 base::RunLoop run_loop_
;
175 DISALLOW_COPY_AND_ASSIGN(WidgetActivationWaiter
);
178 ui::WindowShowState
GetWidgetShowState(const Widget
* widget
) {
179 // Use IsMaximized/IsMinimized/IsFullScreen instead of GetWindowPlacement
180 // because the former is implemented on all platforms but the latter is not.
181 return widget
->IsFullscreen() ? ui::SHOW_STATE_FULLSCREEN
:
182 widget
->IsMaximized() ? ui::SHOW_STATE_MAXIMIZED
:
183 widget
->IsMinimized() ? ui::SHOW_STATE_MINIMIZED
:
184 widget
->IsActive() ? ui::SHOW_STATE_NORMAL
:
185 ui::SHOW_STATE_INACTIVE
;
188 // Give the OS an opportunity to process messages for an activation change, when
189 // there is actually no change expected (e.g. ShowInactive()).
190 void RunPendingMessagesForActiveStatusChange() {
191 #if defined(OS_MACOSX)
192 // On Mac, a single spin is *usually* enough. It isn't when a widget is shown
193 // and made active in two steps, so tests should follow up with a ShowSync()
194 // or ActivateSync to ensure a consistent state.
195 base::RunLoop().RunUntilIdle();
197 // TODO(tapted): Check for desktop aura widgets.
200 // Activate a widget, and wait for it to become active. On non-desktop Aura
201 // this is just an activation. For other widgets, it means activating and then
202 // spinning the run loop until the OS has activated the window.
203 void ActivateSync(Widget
* widget
) {
204 WidgetActivationWaiter
waiter(widget
, true);
209 // Like for ActivateSync(), wait for a widget to become active, but Show() the
210 // widget rather than calling Activate().
211 void ShowSync(Widget
* widget
) {
212 WidgetActivationWaiter
waiter(widget
, true);
217 // Calls ShowInactive() on a Widget, and spins a run loop. The goal is to give
218 // the OS a chance to activate a widget. However, for this case, the test
219 // doesn't expect that to happen, so there is nothing to wait for.
220 void ShowInactiveSync(Widget
* widget
) {
221 widget
->ShowInactive();
222 RunPendingMessagesForActiveStatusChange();
227 class WidgetTestInteractive
: public WidgetTest
{
229 WidgetTestInteractive() {}
230 ~WidgetTestInteractive() override
{}
232 void SetUp() override
{
233 gfx::GLSurface::InitializeOneOffForTests();
234 ui::RegisterPathProvider();
235 base::FilePath ui_test_pak_path
;
236 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK
, &ui_test_pak_path
));
237 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path
);
242 static void ShowQuickMenuImmediately(
243 TouchSelectionControllerImpl
* controller
) {
245 if (controller
->context_menu_timer_
.IsRunning()) {
246 controller
->context_menu_timer_
.Stop();
247 // TODO(tapted): Enable this when porting ui/views/touchui to Mac.
248 #if !defined(OS_MACOSX)
249 controller
->ContextMenuTimerFired();
254 static bool IsQuickMenuVisible(TouchSelectionControllerImpl
* controller
) {
256 return controller
->context_menu_
&& controller
->context_menu_
->visible();
261 // Tests whether activation and focus change works correctly in Windows.
262 // We test the following:-
263 // 1. If the active aura window is correctly set when a top level widget is
265 // 2. If the active aura window in widget 1 created above, is set to NULL when
266 // another top level widget is created and focused.
267 // 3. On focusing the native platform window for widget 1, the active aura
268 // window for widget 1 should be set and that for widget 2 should reset.
269 // TODO(ananta): Discuss with erg on how to write this test for linux x11 aura.
270 TEST_F(WidgetTestInteractive
, DesktopNativeWidgetAuraActivationAndFocusTest
) {
271 // Create widget 1 and expect the active window to be its window.
272 View
* contents_view1
= new View
;
273 contents_view1
->SetFocusable(true);
275 Widget::InitParams init_params
=
276 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
277 init_params
.bounds
= gfx::Rect(0, 0, 200, 200);
278 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
279 init_params
.native_widget
= new DesktopNativeWidgetAura(&widget1
);
280 widget1
.Init(init_params
);
281 widget1
.SetContentsView(contents_view1
);
283 aura::Window
* root_window1
= widget1
.GetNativeView()->GetRootWindow();
284 contents_view1
->RequestFocus();
286 EXPECT_TRUE(root_window1
!= NULL
);
287 aura::client::ActivationClient
* activation_client1
=
288 aura::client::GetActivationClient(root_window1
);
289 EXPECT_TRUE(activation_client1
!= NULL
);
290 EXPECT_EQ(activation_client1
->GetActiveWindow(), widget1
.GetNativeView());
292 // Create widget 2 and expect the active window to be its window.
293 View
* contents_view2
= new View
;
295 Widget::InitParams init_params2
=
296 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
297 init_params2
.bounds
= gfx::Rect(0, 0, 200, 200);
298 init_params2
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
299 init_params2
.native_widget
= new DesktopNativeWidgetAura(&widget2
);
300 widget2
.Init(init_params2
);
301 widget2
.SetContentsView(contents_view2
);
303 aura::Window
* root_window2
= widget2
.GetNativeView()->GetRootWindow();
304 contents_view2
->RequestFocus();
306 root_window2
->GetHost()->GetAcceleratedWidget());
308 aura::client::ActivationClient
* activation_client2
=
309 aura::client::GetActivationClient(root_window2
);
310 EXPECT_TRUE(activation_client2
!= NULL
);
311 EXPECT_EQ(activation_client2
->GetActiveWindow(), widget2
.GetNativeView());
312 EXPECT_EQ(activation_client1
->GetActiveWindow(),
313 reinterpret_cast<aura::Window
*>(NULL
));
315 // Now set focus back to widget 1 and expect the active window to be its
317 contents_view1
->RequestFocus();
319 root_window1
->GetHost()->GetAcceleratedWidget());
320 EXPECT_EQ(activation_client2
->GetActiveWindow(),
321 reinterpret_cast<aura::Window
*>(NULL
));
322 EXPECT_EQ(activation_client1
->GetActiveWindow(), widget1
.GetNativeView());
324 #endif // defined(OS_WIN)
326 TEST_F(WidgetTestInteractive
, CaptureAutoReset
) {
327 Widget
* toplevel
= CreateTopLevelFramelessPlatformWidget();
328 View
* container
= new View
;
329 toplevel
->SetContentsView(container
);
331 EXPECT_FALSE(toplevel
->HasCapture());
332 toplevel
->SetCapture(NULL
);
333 EXPECT_TRUE(toplevel
->HasCapture());
335 // By default, mouse release removes capture.
336 gfx::Point
click_location(45, 15);
337 ui::MouseEvent
release(ui::ET_MOUSE_RELEASED
, click_location
, click_location
,
338 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON
,
339 ui::EF_LEFT_MOUSE_BUTTON
);
340 toplevel
->OnMouseEvent(&release
);
341 EXPECT_FALSE(toplevel
->HasCapture());
343 // Now a mouse release shouldn't remove capture.
344 toplevel
->set_auto_release_capture(false);
345 toplevel
->SetCapture(NULL
);
346 EXPECT_TRUE(toplevel
->HasCapture());
347 toplevel
->OnMouseEvent(&release
);
348 EXPECT_TRUE(toplevel
->HasCapture());
349 toplevel
->ReleaseCapture();
350 EXPECT_FALSE(toplevel
->HasCapture());
353 RunPendingMessages();
356 TEST_F(WidgetTestInteractive
, ResetCaptureOnGestureEnd
) {
357 Widget
* toplevel
= CreateTopLevelFramelessPlatformWidget();
358 View
* container
= new View
;
359 toplevel
->SetContentsView(container
);
361 View
* gesture
= new GestureCaptureView
;
362 gesture
->SetBounds(0, 0, 30, 30);
363 container
->AddChildView(gesture
);
365 MouseView
* mouse
= new MouseView
;
366 mouse
->SetBounds(30, 0, 30, 30);
367 container
->AddChildView(mouse
);
369 toplevel
->SetSize(gfx::Size(100, 100));
372 // Start a gesture on |gesture|.
373 ui::GestureEvent
tap_down(15,
377 ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN
));
378 ui::GestureEvent
end(15,
382 ui::GestureEventDetails(ui::ET_GESTURE_END
));
383 toplevel
->OnGestureEvent(&tap_down
);
385 // Now try to click on |mouse|. Since |gesture| will have capture, |mouse|
386 // will not receive the event.
387 gfx::Point
click_location(45, 15);
389 ui::MouseEvent
press(ui::ET_MOUSE_PRESSED
, click_location
, click_location
,
390 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON
,
391 ui::EF_LEFT_MOUSE_BUTTON
);
392 ui::MouseEvent
release(ui::ET_MOUSE_RELEASED
, click_location
, click_location
,
393 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON
,
394 ui::EF_LEFT_MOUSE_BUTTON
);
396 EXPECT_TRUE(toplevel
->HasCapture());
398 toplevel
->OnMouseEvent(&press
);
399 toplevel
->OnMouseEvent(&release
);
400 EXPECT_EQ(0, mouse
->pressed());
402 EXPECT_FALSE(toplevel
->HasCapture());
404 // The end of the gesture should release the capture, and pressing on |mouse|
405 // should now reach |mouse|.
406 toplevel
->OnGestureEvent(&end
);
407 toplevel
->OnMouseEvent(&press
);
408 toplevel
->OnMouseEvent(&release
);
409 EXPECT_EQ(1, mouse
->pressed());
412 RunPendingMessages();
415 // Checks that if a mouse-press triggers a capture on a different widget (which
416 // consumes the mouse-release event), then the target of the press does not have
418 TEST_F(WidgetTestInteractive
, DisableCaptureWidgetFromMousePress
) {
419 // The test creates two widgets: |first| and |second|.
420 // The View in |first| makes |second| visible, sets capture on it, and starts
421 // a nested loop (like a menu does). The View in |second| terminates the
422 // nested loop and closes the widget.
423 // The test sends a mouse-press event to |first|, and posts a task to send a
424 // release event to |second|, to make sure that the release event is
425 // dispatched after the nested loop starts.
427 Widget
* first
= CreateTopLevelFramelessPlatformWidget();
428 Widget
* second
= CreateTopLevelFramelessPlatformWidget();
430 View
* container
= new NestedLoopCaptureView(second
);
431 first
->SetContentsView(container
);
433 second
->SetContentsView(new ExitLoopOnRelease());
435 first
->SetSize(gfx::Size(100, 100));
438 gfx::Point
location(20, 20);
439 base::MessageLoop::current()->PostTask(
440 FROM_HERE
, base::Bind(&Widget::OnMouseEvent
, base::Unretained(second
),
441 base::Owned(new ui::MouseEvent(
442 ui::ET_MOUSE_RELEASED
, location
, location
,
443 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON
,
444 ui::EF_LEFT_MOUSE_BUTTON
))));
445 ui::MouseEvent
press(ui::ET_MOUSE_PRESSED
, location
, location
,
446 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON
,
447 ui::EF_LEFT_MOUSE_BUTTON
);
448 first
->OnMouseEvent(&press
);
449 EXPECT_FALSE(first
->HasCapture());
451 RunPendingMessages();
454 // Tests some grab/ungrab events.
455 // TODO(estade): can this be enabled now that this is an interactive ui test?
456 TEST_F(WidgetTestInteractive
, DISABLED_GrabUngrab
) {
457 Widget
* toplevel
= CreateTopLevelPlatformWidget();
458 Widget
* child1
= CreateChildNativeWidgetWithParent(toplevel
);
459 Widget
* child2
= CreateChildNativeWidgetWithParent(toplevel
);
461 toplevel
->SetBounds(gfx::Rect(0, 0, 500, 500));
463 child1
->SetBounds(gfx::Rect(10, 10, 300, 300));
464 View
* view
= new MouseView();
465 view
->SetBounds(0, 0, 300, 300);
466 child1
->GetRootView()->AddChildView(view
);
468 child2
->SetBounds(gfx::Rect(200, 10, 200, 200));
469 view
= new MouseView();
470 view
->SetBounds(0, 0, 200, 200);
471 child2
->GetRootView()->AddChildView(view
);
474 RunPendingMessages();
477 gfx::Point
p1(45, 45);
478 ui::MouseEvent
pressed(ui::ET_MOUSE_PRESSED
, p1
, p1
, ui::EventTimeForNow(),
479 ui::EF_LEFT_MOUSE_BUTTON
, ui::EF_LEFT_MOUSE_BUTTON
);
480 toplevel
->OnMouseEvent(&pressed
);
482 EXPECT_TRUE(toplevel
->HasCapture());
483 EXPECT_TRUE(child1
->HasCapture());
484 EXPECT_FALSE(child2
->HasCapture());
486 ui::MouseEvent
released(ui::ET_MOUSE_RELEASED
, p1
, p1
, ui::EventTimeForNow(),
487 ui::EF_LEFT_MOUSE_BUTTON
, ui::EF_LEFT_MOUSE_BUTTON
);
488 toplevel
->OnMouseEvent(&released
);
490 EXPECT_FALSE(toplevel
->HasCapture());
491 EXPECT_FALSE(child1
->HasCapture());
492 EXPECT_FALSE(child2
->HasCapture());
494 RunPendingMessages();
497 gfx::Point
p2(315, 45);
498 ui::MouseEvent
pressed2(ui::ET_MOUSE_PRESSED
, p2
, p2
, ui::EventTimeForNow(),
499 ui::EF_LEFT_MOUSE_BUTTON
, ui::EF_LEFT_MOUSE_BUTTON
);
500 toplevel
->OnMouseEvent(&pressed2
);
501 EXPECT_TRUE(pressed2
.handled());
502 EXPECT_TRUE(toplevel
->HasCapture());
503 EXPECT_TRUE(child2
->HasCapture());
504 EXPECT_FALSE(child1
->HasCapture());
506 ui::MouseEvent
released2(ui::ET_MOUSE_RELEASED
, p2
, p2
, ui::EventTimeForNow(),
507 ui::EF_LEFT_MOUSE_BUTTON
, ui::EF_LEFT_MOUSE_BUTTON
);
508 toplevel
->OnMouseEvent(&released2
);
509 EXPECT_FALSE(toplevel
->HasCapture());
510 EXPECT_FALSE(child1
->HasCapture());
511 EXPECT_FALSE(child2
->HasCapture());
513 toplevel
->CloseNow();
516 // Tests mouse move outside of the window into the "resize controller" and back
517 // will still generate an OnMouseEntered and OnMouseExited event..
518 TEST_F(WidgetTestInteractive
, CheckResizeControllerEvents
) {
519 Widget
* toplevel
= CreateTopLevelPlatformWidget();
521 toplevel
->SetBounds(gfx::Rect(0, 0, 100, 100));
523 MouseView
* view
= new MouseView();
524 view
->SetBounds(90, 90, 10, 10);
525 toplevel
->GetRootView()->AddChildView(view
);
528 RunPendingMessages();
530 // Move to an outside position.
531 gfx::Point
p1(200, 200);
532 ui::MouseEvent
moved_out(ui::ET_MOUSE_MOVED
, p1
, p1
, ui::EventTimeForNow(),
533 ui::EF_NONE
, ui::EF_NONE
);
534 toplevel
->OnMouseEvent(&moved_out
);
535 EXPECT_EQ(0, view
->EnteredCalls());
536 EXPECT_EQ(0, view
->ExitedCalls());
538 // Move onto the active view.
539 gfx::Point
p2(95, 95);
540 ui::MouseEvent
moved_over(ui::ET_MOUSE_MOVED
, p2
, p2
, ui::EventTimeForNow(),
541 ui::EF_NONE
, ui::EF_NONE
);
542 toplevel
->OnMouseEvent(&moved_over
);
543 EXPECT_EQ(1, view
->EnteredCalls());
544 EXPECT_EQ(0, view
->ExitedCalls());
546 // Move onto the outer resizing border.
547 gfx::Point
p3(102, 95);
548 ui::MouseEvent
moved_resizer(ui::ET_MOUSE_MOVED
, p3
, p3
,
549 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
550 toplevel
->OnMouseEvent(&moved_resizer
);
551 EXPECT_EQ(0, view
->EnteredCalls());
552 EXPECT_EQ(1, view
->ExitedCalls());
554 // Move onto the view again.
555 toplevel
->OnMouseEvent(&moved_over
);
556 EXPECT_EQ(1, view
->EnteredCalls());
557 EXPECT_EQ(0, view
->ExitedCalls());
559 RunPendingMessages();
561 toplevel
->CloseNow();
564 // Test view focus restoration when a widget is deactivated and re-activated.
565 TEST_F(WidgetTestInteractive
, ViewFocusOnWidgetActivationChanges
) {
566 Widget
* widget1
= CreateTopLevelPlatformWidget();
567 View
* view1
= new View
;
568 view1
->SetFocusable(true);
569 widget1
->GetContentsView()->AddChildView(view1
);
571 Widget
* widget2
= CreateTopLevelPlatformWidget();
572 View
* view2a
= new View
;
573 View
* view2b
= new View
;
574 view2a
->SetFocusable(true);
575 view2b
->SetFocusable(true);
576 widget2
->GetContentsView()->AddChildView(view2a
);
577 widget2
->GetContentsView()->AddChildView(view2b
);
580 EXPECT_TRUE(widget1
->IsActive());
581 view1
->RequestFocus();
582 EXPECT_EQ(view1
, widget1
->GetFocusManager()->GetFocusedView());
585 EXPECT_TRUE(widget2
->IsActive());
586 EXPECT_FALSE(widget1
->IsActive());
587 EXPECT_EQ(NULL
, widget1
->GetFocusManager()->GetFocusedView());
588 view2a
->RequestFocus();
589 EXPECT_EQ(view2a
, widget2
->GetFocusManager()->GetFocusedView());
590 view2b
->RequestFocus();
591 EXPECT_EQ(view2b
, widget2
->GetFocusManager()->GetFocusedView());
593 ActivateSync(widget1
);
594 EXPECT_TRUE(widget1
->IsActive());
595 EXPECT_EQ(view1
, widget1
->GetFocusManager()->GetFocusedView());
596 EXPECT_FALSE(widget2
->IsActive());
597 EXPECT_EQ(NULL
, widget2
->GetFocusManager()->GetFocusedView());
599 ActivateSync(widget2
);
600 EXPECT_TRUE(widget2
->IsActive());
601 EXPECT_EQ(view2b
, widget2
->GetFocusManager()->GetFocusedView());
602 EXPECT_FALSE(widget1
->IsActive());
603 EXPECT_EQ(NULL
, widget1
->GetFocusManager()->GetFocusedView());
611 // Test view focus retention when a widget's HWND is disabled and re-enabled.
612 TEST_F(WidgetTestInteractive
, ViewFocusOnHWNDEnabledChanges
) {
613 Widget
* widget
= CreateTopLevelFramelessPlatformWidget();
614 widget
->SetContentsView(new View
);
615 for (size_t i
= 0; i
< 2; ++i
) {
616 widget
->GetContentsView()->AddChildView(new View
);
617 widget
->GetContentsView()->child_at(i
)->SetFocusable(true);
621 const HWND hwnd
= HWNDForWidget(widget
);
622 EXPECT_TRUE(::IsWindow(hwnd
));
623 EXPECT_TRUE(::IsWindowEnabled(hwnd
));
624 EXPECT_EQ(hwnd
, ::GetActiveWindow());
626 for (int i
= 0; i
< widget
->GetContentsView()->child_count(); ++i
) {
627 SCOPED_TRACE(base::StringPrintf("Child view %d", i
));
628 View
* view
= widget
->GetContentsView()->child_at(i
);
630 view
->RequestFocus();
631 EXPECT_EQ(view
, widget
->GetFocusManager()->GetFocusedView());
632 EXPECT_FALSE(::EnableWindow(hwnd
, FALSE
));
633 EXPECT_FALSE(::IsWindowEnabled(hwnd
));
635 // Oddly, disabling the HWND leaves it active with the focus unchanged.
636 EXPECT_EQ(hwnd
, ::GetActiveWindow());
637 EXPECT_TRUE(widget
->IsActive());
638 EXPECT_EQ(view
, widget
->GetFocusManager()->GetFocusedView());
640 EXPECT_TRUE(::EnableWindow(hwnd
, TRUE
));
641 EXPECT_TRUE(::IsWindowEnabled(hwnd
));
642 EXPECT_EQ(hwnd
, ::GetActiveWindow());
643 EXPECT_TRUE(widget
->IsActive());
644 EXPECT_EQ(view
, widget
->GetFocusManager()->GetFocusedView());
650 // This class subclasses the Widget class to listen for activation change
651 // notifications and provides accessors to return information as to whether
652 // the widget is active. We need this to ensure that users of the widget
653 // class activate the widget only when the underlying window becomes really
654 // active. Previously we would activate the widget in the WM_NCACTIVATE
655 // message which is incorrect because APIs like FlashWindowEx flash the
656 // window caption by sending fake WM_NCACTIVATE messages.
657 class WidgetActivationTest
: public Widget
{
659 WidgetActivationTest()
662 virtual ~WidgetActivationTest() {}
664 virtual void OnNativeWidgetActivationChanged(bool active
) override
{
668 bool active() const { return active_
; }
673 DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest
);
676 // Tests whether the widget only becomes active when the underlying window
678 TEST_F(WidgetTestInteractive
, WidgetNotActivatedOnFakeActivationMessages
) {
679 WidgetActivationTest widget1
;
680 Widget::InitParams init_params
=
681 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
682 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
683 init_params
.native_widget
= new DesktopNativeWidgetAura(&widget1
);
684 init_params
.bounds
= gfx::Rect(0, 0, 200, 200);
685 widget1
.Init(init_params
);
687 EXPECT_EQ(true, widget1
.active());
689 WidgetActivationTest widget2
;
690 init_params
.native_widget
= new DesktopNativeWidgetAura(&widget2
);
691 widget2
.Init(init_params
);
693 EXPECT_EQ(true, widget2
.active());
694 EXPECT_EQ(false, widget1
.active());
696 HWND win32_native_window1
= HWNDForWidget(&widget1
);
697 EXPECT_TRUE(::IsWindow(win32_native_window1
));
699 ::SendMessage(win32_native_window1
, WM_NCACTIVATE
, 1, 0);
700 EXPECT_EQ(false, widget1
.active());
701 EXPECT_EQ(true, widget2
.active());
703 ::SetActiveWindow(win32_native_window1
);
704 EXPECT_EQ(true, widget1
.active());
705 EXPECT_EQ(false, widget2
.active());
707 #endif // defined(OS_WIN)
709 #if !defined(OS_CHROMEOS)
710 // Provides functionality to create a window modal dialog.
711 class ModalDialogDelegate
: public DialogDelegateView
{
713 explicit ModalDialogDelegate(ui::ModalType type
) : type_(type
) {}
714 ~ModalDialogDelegate() override
{}
716 // WidgetDelegate overrides.
717 ui::ModalType
GetModalType() const override
{ return type_
; }
722 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate
);
725 // Tests whether the focused window is set correctly when a modal window is
726 // created and destroyed. When it is destroyed it should focus the owner window.
727 TEST_F(WidgetTestInteractive
, WindowModalWindowDestroyedActivationTest
) {
728 TestWidgetFocusChangeListener focus_listener
;
729 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener
);
730 const std::vector
<gfx::NativeView
>& focus_changes
=
731 focus_listener
.focus_changes();
733 // Create a top level widget.
734 Widget top_level_widget
;
735 Widget::InitParams init_params
=
736 CreateParams(Widget::InitParams::TYPE_WINDOW
);
737 init_params
.show_state
= ui::SHOW_STATE_NORMAL
;
738 gfx::Rect
initial_bounds(0, 0, 500, 500);
739 init_params
.bounds
= initial_bounds
;
740 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
741 init_params
.native_widget
=
742 new PlatformDesktopNativeWidget(&top_level_widget
);
743 top_level_widget
.Init(init_params
);
744 ShowSync(&top_level_widget
);
746 gfx::NativeView top_level_native_view
= top_level_widget
.GetNativeView();
747 ASSERT_FALSE(focus_listener
.focus_changes().empty());
748 EXPECT_EQ(1u, focus_changes
.size());
749 EXPECT_EQ(top_level_native_view
, focus_changes
[0]);
751 // Create a modal dialog.
752 // This instance will be destroyed when the dialog is destroyed.
753 ModalDialogDelegate
* dialog_delegate
=
754 new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW
);
756 Widget
* modal_dialog_widget
= views::DialogDelegate::CreateDialogWidget(
757 dialog_delegate
, NULL
, top_level_widget
.GetNativeView());
758 modal_dialog_widget
->SetBounds(gfx::Rect(100, 100, 200, 200));
760 // Note the dialog widget doesn't need a ShowSync. Since it is modal, it gains
761 // active status synchronously, even on Mac.
762 modal_dialog_widget
->Show();
764 gfx::NativeView modal_native_view
= modal_dialog_widget
->GetNativeView();
765 EXPECT_EQ(3u, focus_changes
.size());
766 EXPECT_EQ(nullptr, focus_changes
[1]);
767 EXPECT_EQ(modal_native_view
, focus_changes
[2]);
769 #if defined(OS_MACOSX)
770 // Window modal dialogs on Mac are "sheets", which animate to close before
771 // activating their parent widget.
772 WidgetActivationWaiter
waiter(&top_level_widget
, true);
773 modal_dialog_widget
->Close();
776 modal_dialog_widget
->CloseNow();
779 EXPECT_EQ(5u, focus_changes
.size());
780 EXPECT_EQ(nullptr, focus_changes
[3]);
781 EXPECT_EQ(top_level_native_view
, focus_changes
[4]);
783 top_level_widget
.CloseNow();
784 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener
);
787 // Disabled on Mac. Desktop Mac doesn't have system modal windows since Carbon
788 // was deprecated. It does have application modal windows, but only Ash requests
790 #if defined(OS_MACOSX) && !defined(USE_AURA)
791 #define MAYBE_SystemModalWindowReleasesCapture \
792 DISABLED_SystemModalWindowReleasesCapture
794 #define MAYBE_SystemModalWindowReleasesCapture SystemModalWindowReleasesCapture
797 // Test that when opening a system-modal window, capture is released.
798 TEST_F(WidgetTestInteractive
, MAYBE_SystemModalWindowReleasesCapture
) {
799 TestWidgetFocusChangeListener focus_listener
;
800 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener
);
802 // Create a top level widget.
803 Widget top_level_widget
;
804 Widget::InitParams init_params
=
805 CreateParams(Widget::InitParams::TYPE_WINDOW
);
806 init_params
.show_state
= ui::SHOW_STATE_NORMAL
;
807 gfx::Rect
initial_bounds(0, 0, 500, 500);
808 init_params
.bounds
= initial_bounds
;
809 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
810 init_params
.native_widget
=
811 new PlatformDesktopNativeWidget(&top_level_widget
);
812 top_level_widget
.Init(init_params
);
813 ShowSync(&top_level_widget
);
815 ASSERT_FALSE(focus_listener
.focus_changes().empty());
816 EXPECT_EQ(top_level_widget
.GetNativeView(),
817 focus_listener
.focus_changes().back());;
819 EXPECT_FALSE(top_level_widget
.HasCapture());
820 top_level_widget
.SetCapture(NULL
);
821 EXPECT_TRUE(top_level_widget
.HasCapture());
823 // Create a modal dialog.
824 ModalDialogDelegate
* dialog_delegate
=
825 new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM
);
827 Widget
* modal_dialog_widget
= views::DialogDelegate::CreateDialogWidget(
828 dialog_delegate
, NULL
, top_level_widget
.GetNativeView());
829 modal_dialog_widget
->SetBounds(gfx::Rect(100, 100, 200, 200));
830 ShowSync(modal_dialog_widget
);
832 EXPECT_FALSE(top_level_widget
.HasCapture());
834 modal_dialog_widget
->CloseNow();
835 top_level_widget
.CloseNow();
836 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener
);
839 #endif // !defined(OS_CHROMEOS)
841 TEST_F(WidgetTestInteractive
, CanActivateFlagIsHonored
) {
843 Widget::InitParams init_params
=
844 CreateParams(Widget::InitParams::TYPE_WINDOW
);
845 init_params
.bounds
= gfx::Rect(0, 0, 200, 200);
846 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
847 init_params
.activatable
= Widget::InitParams::ACTIVATABLE_NO
;
848 #if !defined(OS_CHROMEOS)
849 init_params
.native_widget
= new PlatformDesktopNativeWidget(&widget
);
850 #endif // !defined(OS_CHROMEOS)
851 widget
.Init(init_params
);
854 EXPECT_FALSE(widget
.IsActive());
857 // No touch on desktop Mac. Tracked in http://crbug.com/445520.
858 #if defined(OS_MACOSX) && !defined(USE_AURA)
859 #define MAYBE_TouchSelectionQuickMenuIsNotActivated \
860 DISABLED_TouchSelectionQuickMenuIsNotActivated
862 #define MAYBE_TouchSelectionQuickMenuIsNotActivated \
863 TouchSelectionQuickMenuIsNotActivated
866 // Test that touch selection quick menu is not activated when opened.
867 TEST_F(WidgetTestInteractive
, MAYBE_TouchSelectionQuickMenuIsNotActivated
) {
868 base::CommandLine::ForCurrentProcess()->AppendSwitch(
869 switches::kEnableTouchEditing
);
871 views_delegate().set_use_desktop_native_widgets(true);
872 #endif // !defined(OS_WIN)
875 Widget::InitParams init_params
=
876 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
877 init_params
.bounds
= gfx::Rect(0, 0, 200, 200);
878 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
879 widget
.Init(init_params
);
881 Textfield
* textfield
= new Textfield
;
882 textfield
->SetBounds(0, 0, 200, 20);
883 textfield
->SetText(base::ASCIIToUTF16("some text"));
884 widget
.GetRootView()->AddChildView(textfield
);
887 textfield
->RequestFocus();
888 textfield
->SelectAll(true);
889 TextfieldTestApi
textfield_test_api(textfield
);
891 RunPendingMessages();
893 ui::test::EventGenerator
generator(widget
.GetNativeWindow());
894 generator
.GestureTapAt(gfx::Point(10, 10));
895 ShowQuickMenuImmediately(static_cast<TouchSelectionControllerImpl
*>(
896 textfield_test_api
.touch_selection_controller()));
898 EXPECT_TRUE(textfield
->HasFocus());
899 EXPECT_TRUE(widget
.IsActive());
900 EXPECT_TRUE(IsQuickMenuVisible(static_cast<TouchSelectionControllerImpl
*>(
901 textfield_test_api
.touch_selection_controller())));
904 TEST_F(WidgetTestInteractive
, DisableViewDoesNotActivateWidget
) {
906 views_delegate().set_use_desktop_native_widgets(true);
907 #endif // !defined(OS_WIN)
909 // Create first widget and view, activate the widget, and focus the view.
911 Widget::InitParams params1
= CreateParams(Widget::InitParams::TYPE_POPUP
);
912 params1
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
913 params1
.activatable
= Widget::InitParams::ACTIVATABLE_YES
;
914 widget1
.Init(params1
);
916 View
* view1
= new View();
917 view1
->SetFocusable(true);
918 widget1
.GetRootView()->AddChildView(view1
);
920 ActivateSync(&widget1
);
922 FocusManager
* focus_manager1
= widget1
.GetFocusManager();
923 ASSERT_TRUE(focus_manager1
);
924 focus_manager1
->SetFocusedView(view1
);
925 EXPECT_EQ(view1
, focus_manager1
->GetFocusedView());
927 // Create second widget and view, activate the widget, and focus the view.
929 Widget::InitParams params2
= CreateParams(Widget::InitParams::TYPE_POPUP
);
930 params2
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
931 params2
.activatable
= Widget::InitParams::ACTIVATABLE_YES
;
932 widget2
.Init(params2
);
934 View
* view2
= new View();
935 view2
->SetFocusable(true);
936 widget2
.GetRootView()->AddChildView(view2
);
938 ActivateSync(&widget2
);
939 EXPECT_TRUE(widget2
.IsActive());
940 EXPECT_FALSE(widget1
.IsActive());
942 FocusManager
* focus_manager2
= widget2
.GetFocusManager();
943 ASSERT_TRUE(focus_manager2
);
944 focus_manager2
->SetFocusedView(view2
);
945 EXPECT_EQ(view2
, focus_manager2
->GetFocusedView());
947 // Disable the first view and make sure it loses focus, but its widget is not
949 view1
->SetEnabled(false);
950 EXPECT_NE(view1
, focus_manager1
->GetFocusedView());
951 EXPECT_FALSE(widget1
.IsActive());
952 EXPECT_TRUE(widget2
.IsActive());
955 TEST_F(WidgetTestInteractive
, ShowCreatesActiveWindow
) {
956 Widget
* widget
= CreateTopLevelPlatformWidget();
959 EXPECT_EQ(GetWidgetShowState(widget
), ui::SHOW_STATE_NORMAL
);
964 TEST_F(WidgetTestInteractive
, ShowInactive
) {
965 Widget
* widget
= CreateTopLevelPlatformWidget();
967 ShowInactiveSync(widget
);
968 EXPECT_EQ(GetWidgetShowState(widget
), ui::SHOW_STATE_INACTIVE
);
973 TEST_F(WidgetTestInteractive
, InactiveBeforeShow
) {
974 Widget
* widget
= CreateTopLevelPlatformWidget();
976 EXPECT_FALSE(widget
->IsActive());
977 EXPECT_FALSE(widget
->IsVisible());
981 EXPECT_TRUE(widget
->IsActive());
982 EXPECT_TRUE(widget
->IsVisible());
987 TEST_F(WidgetTestInteractive
, ShowInactiveAfterShow
) {
988 // Create 2 widgets to ensure window layering does not change.
989 Widget
* widget
= CreateTopLevelPlatformWidget();
990 Widget
* widget2
= CreateTopLevelPlatformWidget();
993 EXPECT_FALSE(widget
->IsActive());
994 EXPECT_TRUE(widget2
->IsVisible());
995 EXPECT_TRUE(widget2
->IsActive());
998 EXPECT_TRUE(widget
->IsActive());
999 EXPECT_FALSE(widget2
->IsActive());
1001 ShowInactiveSync(widget
);
1002 EXPECT_TRUE(widget
->IsActive());
1003 EXPECT_FALSE(widget2
->IsActive());
1004 EXPECT_EQ(GetWidgetShowState(widget
), ui::SHOW_STATE_NORMAL
);
1006 widget2
->CloseNow();
1010 TEST_F(WidgetTestInteractive
, ShowAfterShowInactive
) {
1011 Widget
* widget
= CreateTopLevelPlatformWidget();
1012 widget
->SetBounds(gfx::Rect(100, 100, 100, 100));
1014 ShowInactiveSync(widget
);
1016 EXPECT_EQ(GetWidgetShowState(widget
), ui::SHOW_STATE_NORMAL
);
1021 #if !defined(OS_CHROMEOS)
1022 TEST_F(WidgetTestInteractive
, InactiveWidgetDoesNotGrabActivation
) {
1023 Widget
* widget
= CreateTopLevelPlatformWidget();
1025 EXPECT_EQ(GetWidgetShowState(widget
), ui::SHOW_STATE_NORMAL
);
1028 Widget::InitParams params
= CreateParams(Widget::InitParams::TYPE_POPUP
);
1029 params
.native_widget
= new PlatformDesktopNativeWidget(&widget2
);
1030 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1031 widget2
.Init(params
);
1033 RunPendingMessagesForActiveStatusChange();
1035 EXPECT_EQ(GetWidgetShowState(&widget2
), ui::SHOW_STATE_INACTIVE
);
1036 EXPECT_EQ(GetWidgetShowState(widget
), ui::SHOW_STATE_NORMAL
);
1041 #endif // !defined(OS_CHROMEOS)
1043 // ExitFullscreenRestoreState doesn't use DesktopAura widgets. On Mac, there are
1044 // currently only Desktop widgets and fullscreen changes have to coordinate with
1045 // the OS. See BridgedNativeWidgetUITest for native Mac fullscreen tests.
1046 // Maximize on mac is also (intentionally) a no-op.
1047 #if defined(OS_MACOSX) && !defined(USE_AURA)
1048 #define MAYBE_ExitFullscreenRestoreState DISABLED_ExitFullscreenRestoreState
1050 #define MAYBE_ExitFullscreenRestoreState ExitFullscreenRestoreState
1053 // Test that window state is not changed after getting out of full screen.
1054 TEST_F(WidgetTestInteractive
, MAYBE_ExitFullscreenRestoreState
) {
1055 Widget
* toplevel
= CreateTopLevelPlatformWidget();
1058 RunPendingMessages();
1060 // This should be a normal state window.
1061 EXPECT_EQ(ui::SHOW_STATE_NORMAL
, GetWidgetShowState(toplevel
));
1063 toplevel
->SetFullscreen(true);
1064 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN
, GetWidgetShowState(toplevel
));
1065 toplevel
->SetFullscreen(false);
1066 EXPECT_NE(ui::SHOW_STATE_FULLSCREEN
, GetWidgetShowState(toplevel
));
1068 // And it should still be in normal state after getting out of full screen.
1069 EXPECT_EQ(ui::SHOW_STATE_NORMAL
, GetWidgetShowState(toplevel
));
1071 // Now, make it maximized.
1072 toplevel
->Maximize();
1073 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED
, GetWidgetShowState(toplevel
));
1075 toplevel
->SetFullscreen(true);
1076 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN
, GetWidgetShowState(toplevel
));
1077 toplevel
->SetFullscreen(false);
1078 EXPECT_NE(ui::SHOW_STATE_FULLSCREEN
, GetWidgetShowState(toplevel
));
1080 // And it stays maximized after getting out of full screen.
1081 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED
, GetWidgetShowState(toplevel
));
1085 RunPendingMessages();
1090 // Used to veirfy OnMouseCaptureLost() has been invoked.
1091 class CaptureLostTrackingWidget
: public Widget
{
1093 CaptureLostTrackingWidget() : got_capture_lost_(false) {}
1094 ~CaptureLostTrackingWidget() override
{}
1096 bool GetAndClearGotCaptureLost() {
1097 bool value
= got_capture_lost_
;
1098 got_capture_lost_
= false;
1103 void OnMouseCaptureLost() override
{
1104 got_capture_lost_
= true;
1105 Widget::OnMouseCaptureLost();
1109 bool got_capture_lost_
;
1111 DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget
);
1116 class WidgetCaptureTest
: public ViewsTestBase
{
1118 WidgetCaptureTest() {
1121 ~WidgetCaptureTest() override
{}
1123 void SetUp() override
{
1124 gfx::GLSurface::InitializeOneOffForTests();
1125 ui::RegisterPathProvider();
1126 base::FilePath ui_test_pak_path
;
1127 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK
, &ui_test_pak_path
));
1128 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path
);
1129 ViewsTestBase::SetUp();
1132 // Verifies Widget::SetCapture() results in updating native capture along with
1133 // invoking the right Widget function.
1134 void TestCapture(bool use_desktop_native_widget
) {
1135 CaptureLostTrackingWidget widget1
;
1136 Widget::InitParams params1
=
1137 CreateParams(views::Widget::InitParams::TYPE_WINDOW
);
1138 params1
.native_widget
= CreateNativeWidget(use_desktop_native_widget
,
1140 params1
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1141 widget1
.Init(params1
);
1144 CaptureLostTrackingWidget widget2
;
1145 Widget::InitParams params2
=
1146 CreateParams(views::Widget::InitParams::TYPE_WINDOW
);
1147 params2
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1148 params2
.native_widget
= CreateNativeWidget(use_desktop_native_widget
,
1150 widget2
.Init(params2
);
1153 // Set capture to widget2 and verity it gets it.
1154 widget2
.SetCapture(widget2
.GetRootView());
1155 EXPECT_FALSE(widget1
.HasCapture());
1156 EXPECT_TRUE(widget2
.HasCapture());
1157 EXPECT_FALSE(widget1
.GetAndClearGotCaptureLost());
1158 EXPECT_FALSE(widget2
.GetAndClearGotCaptureLost());
1160 // Set capture to widget1 and verify it gets it.
1161 widget1
.SetCapture(widget1
.GetRootView());
1162 EXPECT_TRUE(widget1
.HasCapture());
1163 EXPECT_FALSE(widget2
.HasCapture());
1164 EXPECT_FALSE(widget1
.GetAndClearGotCaptureLost());
1165 EXPECT_TRUE(widget2
.GetAndClearGotCaptureLost());
1167 // Release and verify no one has it.
1168 widget1
.ReleaseCapture();
1169 EXPECT_FALSE(widget1
.HasCapture());
1170 EXPECT_FALSE(widget2
.HasCapture());
1171 EXPECT_TRUE(widget1
.GetAndClearGotCaptureLost());
1172 EXPECT_FALSE(widget2
.GetAndClearGotCaptureLost());
1175 NativeWidget
* CreateNativeWidget(bool create_desktop_native_widget
,
1177 #if !defined(OS_CHROMEOS)
1178 if (create_desktop_native_widget
)
1179 return new PlatformDesktopNativeWidget(widget
);
1185 DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest
);
1188 // See description in TestCapture().
1189 TEST_F(WidgetCaptureTest
, Capture
) {
1193 #if !defined(OS_CHROMEOS)
1194 // See description in TestCapture(). Creates DesktopNativeWidget.
1195 TEST_F(WidgetCaptureTest
, CaptureDesktopNativeWidget
) {
1200 // Test that no state is set if capture fails.
1201 TEST_F(WidgetCaptureTest
, FailedCaptureRequestIsNoop
) {
1203 Widget::InitParams params
=
1204 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
1205 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1206 params
.bounds
= gfx::Rect(400, 400);
1207 widget
.Init(params
);
1209 MouseView
* mouse_view1
= new MouseView
;
1210 MouseView
* mouse_view2
= new MouseView
;
1211 View
* contents_view
= new View
;
1212 contents_view
->AddChildView(mouse_view1
);
1213 contents_view
->AddChildView(mouse_view2
);
1214 widget
.SetContentsView(contents_view
);
1216 mouse_view1
->SetBounds(0, 0, 200, 400);
1217 mouse_view2
->SetBounds(200, 0, 200, 400);
1219 // Setting capture should fail because |widget| is not visible.
1220 widget
.SetCapture(mouse_view1
);
1221 EXPECT_FALSE(widget
.HasCapture());
1224 ui::test::EventGenerator
generator(GetContext(), widget
.GetNativeWindow());
1225 generator
.set_current_location(gfx::Point(300, 10));
1226 generator
.PressLeftButton();
1228 EXPECT_FALSE(mouse_view1
->pressed());
1229 EXPECT_TRUE(mouse_view2
->pressed());
1232 // Regression test for http://crbug.com/382421 (Linux-Aura issue).
1233 // TODO(pkotwicz): Make test pass on CrOS and Windows.
1234 // TODO(tapted): Investigate for toolkit-views on Mac http;//crbug.com/441064.
1235 #if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_MACOSX)
1236 #define MAYBE_MouseExitOnCaptureGrab DISABLED_MouseExitOnCaptureGrab
1238 #define MAYBE_MouseExitOnCaptureGrab MouseExitOnCaptureGrab
1241 // Test that a synthetic mouse exit is sent to the widget which was handling
1242 // mouse events when a different widget grabs capture.
1243 TEST_F(WidgetCaptureTest
, MAYBE_MouseExitOnCaptureGrab
) {
1245 Widget::InitParams params1
=
1246 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
1247 params1
.native_widget
= CreateNativeWidget(true, &widget1
);
1248 params1
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1249 widget1
.Init(params1
);
1250 MouseView
* mouse_view1
= new MouseView
;
1251 widget1
.SetContentsView(mouse_view1
);
1253 widget1
.SetBounds(gfx::Rect(300, 300));
1256 Widget::InitParams params2
=
1257 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
1258 params2
.native_widget
= CreateNativeWidget(true, &widget2
);
1259 params2
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1260 widget2
.Init(params2
);
1262 widget2
.SetBounds(gfx::Rect(400, 0, 300, 300));
1264 ui::test::EventGenerator
generator(widget1
.GetNativeWindow());
1265 generator
.set_current_location(gfx::Point(100, 100));
1266 generator
.MoveMouseBy(0, 0);
1268 EXPECT_EQ(1, mouse_view1
->EnteredCalls());
1269 EXPECT_EQ(0, mouse_view1
->ExitedCalls());
1271 widget2
.SetCapture(NULL
);
1272 EXPECT_EQ(0, mouse_view1
->EnteredCalls());
1273 // Grabbing native capture on Windows generates a ui::ET_MOUSE_EXITED event
1274 // in addition to the one generated by Chrome.
1275 EXPECT_LT(0, mouse_view1
->ExitedCalls());
1280 // Widget observer which grabs capture when the widget is activated.
1281 class CaptureOnActivationObserver
: public WidgetObserver
{
1283 CaptureOnActivationObserver() : activation_observed_(false) {}
1284 ~CaptureOnActivationObserver() override
{}
1287 void OnWidgetActivationChanged(Widget
* widget
, bool active
) override
{
1289 widget
->SetCapture(nullptr);
1290 activation_observed_
= true;
1294 bool activation_observed() const { return activation_observed_
; }
1297 bool activation_observed_
;
1299 DISALLOW_COPY_AND_ASSIGN(CaptureOnActivationObserver
);
1304 // Test that setting capture on widget activation of a non-toplevel widget
1305 // (e.g. a bubble on Linux) succeeds.
1306 TEST_F(WidgetCaptureTest
, SetCaptureToNonToplevel
) {
1308 Widget::InitParams toplevel_params
=
1309 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
1310 toplevel_params
.native_widget
= CreateNativeWidget(true, &toplevel
);
1311 toplevel_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1312 toplevel
.Init(toplevel_params
);
1315 Widget
* child
= new Widget
;
1316 Widget::InitParams child_params
=
1317 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
1318 child_params
.parent
= toplevel
.GetNativeView();
1319 child_params
.context
= toplevel
.GetNativeWindow();
1320 child
->Init(child_params
);
1322 CaptureOnActivationObserver observer
;
1323 child
->AddObserver(&observer
);
1326 #if defined(OS_MACOSX) && !defined(USE_AURA)
1327 // On Mac, activation is asynchronous. A single trip to the runloop should be
1328 // sufficient. On Aura platforms, note that since the child widget isn't top-
1329 // level, the aura window manager gets asked whether the widget is active, not
1331 base::RunLoop().RunUntilIdle();
1334 EXPECT_TRUE(observer
.activation_observed());
1335 EXPECT_TRUE(child
->HasCapture());
1342 // Used to verify OnMouseEvent() has been invoked.
1343 class MouseEventTrackingWidget
: public Widget
{
1345 MouseEventTrackingWidget() : got_mouse_event_(false) {}
1346 virtual ~MouseEventTrackingWidget() {}
1348 bool GetAndClearGotMouseEvent() {
1349 bool value
= got_mouse_event_
;
1350 got_mouse_event_
= false;
1355 virtual void OnMouseEvent(ui::MouseEvent
* event
) override
{
1356 got_mouse_event_
= true;
1357 Widget::OnMouseEvent(event
);
1361 bool got_mouse_event_
;
1363 DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget
);
1368 // Verifies if a mouse event is received on a widget that doesn't have capture
1369 // on Windows that it is correctly processed by the widget that doesn't have
1370 // capture. This behavior is not desired on OSes other than Windows.
1371 TEST_F(WidgetCaptureTest
, MouseEventDispatchedToRightWindow
) {
1372 MouseEventTrackingWidget widget1
;
1373 Widget::InitParams params1
=
1374 CreateParams(views::Widget::InitParams::TYPE_WINDOW
);
1375 params1
.native_widget
= new DesktopNativeWidgetAura(&widget1
);
1376 params1
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1377 widget1
.Init(params1
);
1380 MouseEventTrackingWidget widget2
;
1381 Widget::InitParams params2
=
1382 CreateParams(views::Widget::InitParams::TYPE_WINDOW
);
1383 params2
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1384 params2
.native_widget
= new DesktopNativeWidgetAura(&widget2
);
1385 widget2
.Init(params2
);
1388 // Set capture to widget2 and verity it gets it.
1389 widget2
.SetCapture(widget2
.GetRootView());
1390 EXPECT_FALSE(widget1
.HasCapture());
1391 EXPECT_TRUE(widget2
.HasCapture());
1393 widget1
.GetAndClearGotMouseEvent();
1394 widget2
.GetAndClearGotMouseEvent();
1395 // Send a mouse event to the RootWindow associated with |widget1|. Even though
1396 // |widget2| has capture, |widget1| should still get the event.
1397 ui::MouseEvent
mouse_event(ui::ET_MOUSE_EXITED
, gfx::Point(), gfx::Point(),
1398 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
1399 ui::EventDispatchDetails details
= widget1
.GetNativeWindow()->
1400 GetHost()->event_processor()->OnEventFromSource(&mouse_event
);
1401 ASSERT_FALSE(details
.dispatcher_destroyed
);
1402 EXPECT_TRUE(widget1
.GetAndClearGotMouseEvent());
1403 EXPECT_FALSE(widget2
.GetAndClearGotMouseEvent());
1405 #endif // defined(OS_WIN)
1408 } // namespace views