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/ime/input_method.h"
13 #include "ui/base/ime/text_input_client.h"
14 #include "ui/base/resource/resource_bundle.h"
15 #include "ui/base/ui_base_paths.h"
16 #include "ui/base/ui_base_switches.h"
17 #include "ui/events/event_processor.h"
18 #include "ui/events/event_utils.h"
19 #include "ui/events/test/event_generator.h"
20 #include "ui/gfx/native_widget_types.h"
21 #include "ui/gl/test/gl_surface_test_support.h"
22 #include "ui/views/controls/textfield/textfield.h"
23 #include "ui/views/controls/textfield/textfield_test_api.h"
24 #include "ui/views/focus/focus_manager.h"
25 #include "ui/views/test/focus_manager_test.h"
26 #include "ui/views/test/widget_test.h"
27 #include "ui/views/touchui/touch_selection_controller_impl.h"
28 #include "ui/views/widget/widget.h"
29 #include "ui/views/window/dialog_delegate.h"
30 #include "ui/wm/public/activation_client.h"
33 #include "ui/aura/window.h"
34 #include "ui/aura/window_tree_host.h"
35 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
36 #include "ui/views/win/hwnd_util.h"
44 // A View that closes the Widget and exits the current message-loop when it
45 // receives a mouse-release event.
46 class ExitLoopOnRelease
: public View
{
48 ExitLoopOnRelease() {}
49 ~ExitLoopOnRelease() override
{}
52 // Overridden from View:
53 void OnMouseReleased(const ui::MouseEvent
& event
) override
{
55 base::MessageLoop::current()->QuitNow();
58 DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease
);
61 // A view that does a capture on ui::ET_GESTURE_TAP_DOWN events.
62 class GestureCaptureView
: public View
{
64 GestureCaptureView() {}
65 ~GestureCaptureView() override
{}
68 // Overridden from View:
69 void OnGestureEvent(ui::GestureEvent
* event
) override
{
70 if (event
->type() == ui::ET_GESTURE_TAP_DOWN
) {
71 GetWidget()->SetCapture(this);
72 event
->StopPropagation();
76 DISALLOW_COPY_AND_ASSIGN(GestureCaptureView
);
79 // A view that always processes all mouse events.
80 class MouseView
: public View
{
88 ~MouseView() override
{}
90 bool OnMousePressed(const ui::MouseEvent
& event
) override
{
95 void OnMouseEntered(const ui::MouseEvent
& event
) override
{ entered_
++; }
97 void OnMouseExited(const ui::MouseEvent
& event
) override
{ exited_
++; }
99 // Return the number of OnMouseEntered calls and reset the counter.
106 // Return the number of OnMouseExited calls and reset the counter.
113 int pressed() const { return pressed_
; }
121 DISALLOW_COPY_AND_ASSIGN(MouseView
);
124 // A View that shows a different widget, sets capture on that widget, and
125 // initiates a nested message-loop when it receives a mouse-press event.
126 class NestedLoopCaptureView
: public View
{
128 explicit NestedLoopCaptureView(Widget
* widget
) : widget_(widget
) {}
129 ~NestedLoopCaptureView() override
{}
132 // Overridden from View:
133 bool OnMousePressed(const ui::MouseEvent
& event
) override
{
134 // Start a nested loop.
136 widget_
->SetCapture(widget_
->GetContentsView());
137 EXPECT_TRUE(widget_
->HasCapture());
139 base::MessageLoopForUI
* loop
= base::MessageLoopForUI::current();
140 base::MessageLoop::ScopedNestableTaskAllower
allow(loop
);
142 base::RunLoop run_loop
;
149 DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView
);
152 // Spins a run loop until a Widget's active state matches a desired state.
153 class WidgetActivationWaiter
: public WidgetObserver
{
155 WidgetActivationWaiter(Widget
* widget
, bool active
) : observed_(false) {
157 // On Windows, a HWND can receive a WM_ACTIVATE message without the value
158 // of ::GetActiveWindow() updating to reflect that change. This can cause
159 // the active window reported by IsActive() to get out of sync. Usually this
160 // happens after a call to HWNDMessageHandler::Deactivate() which works by
161 // activating some other window, which might be in another application.
162 // Doing this can trigger the native OS activation-blocker, causing the
163 // taskbar icon to flash instead. But since activation of native widgets on
164 // Windows is synchronous, we never have to wait anyway, so it's safe to
166 if (active
== widget
->IsActive()) {
171 // Always expect a change for tests using this.
172 EXPECT_NE(active
, widget
->IsActive());
173 widget
->AddObserver(this);
181 void OnWidgetActivationChanged(Widget
* widget
, bool active
) override
{
183 widget
->RemoveObserver(this);
184 if (run_loop_
.running())
189 base::RunLoop run_loop_
;
192 DISALLOW_COPY_AND_ASSIGN(WidgetActivationWaiter
);
195 ui::WindowShowState
GetWidgetShowState(const Widget
* widget
) {
196 // Use IsMaximized/IsMinimized/IsFullScreen instead of GetWindowPlacement
197 // because the former is implemented on all platforms but the latter is not.
198 return widget
->IsFullscreen() ? ui::SHOW_STATE_FULLSCREEN
:
199 widget
->IsMaximized() ? ui::SHOW_STATE_MAXIMIZED
:
200 widget
->IsMinimized() ? ui::SHOW_STATE_MINIMIZED
:
201 widget
->IsActive() ? ui::SHOW_STATE_NORMAL
:
202 ui::SHOW_STATE_INACTIVE
;
205 // Give the OS an opportunity to process messages for an activation change, when
206 // there is actually no change expected (e.g. ShowInactive()).
207 void RunPendingMessagesForActiveStatusChange() {
208 #if defined(OS_MACOSX)
209 // On Mac, a single spin is *usually* enough. It isn't when a widget is shown
210 // and made active in two steps, so tests should follow up with a ShowSync()
211 // or ActivateSync to ensure a consistent state.
212 base::RunLoop().RunUntilIdle();
214 // TODO(tapted): Check for desktop aura widgets.
217 // Activate a widget, and wait for it to become active. On non-desktop Aura
218 // this is just an activation. For other widgets, it means activating and then
219 // spinning the run loop until the OS has activated the window.
220 void ActivateSync(Widget
* widget
) {
221 WidgetActivationWaiter
waiter(widget
, true);
226 // Like for ActivateSync(), wait for a widget to become active, but Show() the
227 // widget rather than calling Activate().
228 void ShowSync(Widget
* widget
) {
229 WidgetActivationWaiter
waiter(widget
, true);
234 void DeactivateSync(Widget
* widget
) {
235 #if defined(OS_MACOSX)
236 // Deactivation of a window isn't a concept on Mac: If an application is
237 // active and it has any activatable windows, then one of them is always
238 // active. But we can simulate deactivation (e.g. as if another application
239 // became active) by temporarily making |widget| non-activatable, then
240 // activating (and closing) a temporary widget.
241 widget
->widget_delegate()->set_can_activate(false);
242 Widget
* stealer
= new Widget
;
243 stealer
->Init(Widget::InitParams(Widget::InitParams::TYPE_WINDOW
));
246 widget
->widget_delegate()->set_can_activate(true);
248 WidgetActivationWaiter
waiter(widget
, false);
249 widget
->Deactivate();
255 void ActivatePlatformWindow(Widget
* widget
) {
257 widget
->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
261 // Calls ShowInactive() on a Widget, and spins a run loop. The goal is to give
262 // the OS a chance to activate a widget. However, for this case, the test
263 // doesn't expect that to happen, so there is nothing to wait for.
264 void ShowInactiveSync(Widget
* widget
) {
265 widget
->ShowInactive();
266 RunPendingMessagesForActiveStatusChange();
271 class WidgetTestInteractive
: public WidgetTest
{
273 WidgetTestInteractive() {}
274 ~WidgetTestInteractive() override
{}
276 void SetUp() override
{
277 gfx::GLSurfaceTestSupport::InitializeOneOff();
278 ui::RegisterPathProvider();
279 base::FilePath ui_test_pak_path
;
280 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK
, &ui_test_pak_path
));
281 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path
);
286 #if defined(USE_AURA)
287 static void ShowQuickMenuImmediately(
288 TouchSelectionControllerImpl
* controller
) {
290 if (controller
->quick_menu_timer_
.IsRunning()) {
291 controller
->quick_menu_timer_
.Stop();
292 controller
->QuickMenuTimerFired();
295 #endif // defined (USE_AURA)
297 Widget
* CreateWidget() {
298 Widget
* widget
= CreateNativeDesktopWidget();
299 widget
->SetBounds(gfx::Rect(0, 0, 200, 200));
305 // Tests whether activation and focus change works correctly in Windows.
306 // We test the following:-
307 // 1. If the active aura window is correctly set when a top level widget is
309 // 2. If the active aura window in widget 1 created above, is set to NULL when
310 // another top level widget is created and focused.
311 // 3. On focusing the native platform window for widget 1, the active aura
312 // window for widget 1 should be set and that for widget 2 should reset.
313 // TODO(ananta): Discuss with erg on how to write this test for linux x11 aura.
314 TEST_F(WidgetTestInteractive
, DesktopNativeWidgetAuraActivationAndFocusTest
) {
315 // Create widget 1 and expect the active window to be its window.
316 View
* focusable_view1
= new View
;
317 focusable_view1
->SetFocusable(true);
318 Widget
* widget1
= CreateWidget();
319 widget1
->GetContentsView()->AddChildView(focusable_view1
);
321 aura::Window
* root_window1
= widget1
->GetNativeView()->GetRootWindow();
322 focusable_view1
->RequestFocus();
324 EXPECT_TRUE(root_window1
!= NULL
);
325 aura::client::ActivationClient
* activation_client1
=
326 aura::client::GetActivationClient(root_window1
);
327 EXPECT_TRUE(activation_client1
!= NULL
);
328 EXPECT_EQ(activation_client1
->GetActiveWindow(), widget1
->GetNativeView());
330 // Create widget 2 and expect the active window to be its window.
331 View
* focusable_view2
= new View
;
332 Widget
* widget2
= CreateWidget();
333 widget1
->GetContentsView()->AddChildView(focusable_view2
);
335 aura::Window
* root_window2
= widget2
->GetNativeView()->GetRootWindow();
336 focusable_view2
->RequestFocus();
337 ActivatePlatformWindow(widget2
);
339 aura::client::ActivationClient
* activation_client2
=
340 aura::client::GetActivationClient(root_window2
);
341 EXPECT_TRUE(activation_client2
!= NULL
);
342 EXPECT_EQ(activation_client2
->GetActiveWindow(), widget2
->GetNativeView());
343 EXPECT_EQ(activation_client1
->GetActiveWindow(),
344 reinterpret_cast<aura::Window
*>(NULL
));
346 // Now set focus back to widget 1 and expect the active window to be its
348 focusable_view1
->RequestFocus();
349 ActivatePlatformWindow(widget1
);
350 EXPECT_EQ(activation_client2
->GetActiveWindow(),
351 reinterpret_cast<aura::Window
*>(NULL
));
352 EXPECT_EQ(activation_client1
->GetActiveWindow(), widget1
->GetNativeView());
357 #endif // defined(OS_WIN)
359 TEST_F(WidgetTestInteractive
, CaptureAutoReset
) {
360 Widget
* toplevel
= CreateTopLevelFramelessPlatformWidget();
361 View
* container
= new View
;
362 toplevel
->SetContentsView(container
);
364 EXPECT_FALSE(toplevel
->HasCapture());
365 toplevel
->SetCapture(NULL
);
366 EXPECT_TRUE(toplevel
->HasCapture());
368 // By default, mouse release removes capture.
369 gfx::Point
click_location(45, 15);
370 ui::MouseEvent
release(ui::ET_MOUSE_RELEASED
, click_location
, click_location
,
371 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON
,
372 ui::EF_LEFT_MOUSE_BUTTON
);
373 toplevel
->OnMouseEvent(&release
);
374 EXPECT_FALSE(toplevel
->HasCapture());
376 // Now a mouse release shouldn't remove capture.
377 toplevel
->set_auto_release_capture(false);
378 toplevel
->SetCapture(NULL
);
379 EXPECT_TRUE(toplevel
->HasCapture());
380 toplevel
->OnMouseEvent(&release
);
381 EXPECT_TRUE(toplevel
->HasCapture());
382 toplevel
->ReleaseCapture();
383 EXPECT_FALSE(toplevel
->HasCapture());
386 RunPendingMessages();
389 TEST_F(WidgetTestInteractive
, ResetCaptureOnGestureEnd
) {
390 Widget
* toplevel
= CreateTopLevelFramelessPlatformWidget();
391 View
* container
= new View
;
392 toplevel
->SetContentsView(container
);
394 View
* gesture
= new GestureCaptureView
;
395 gesture
->SetBounds(0, 0, 30, 30);
396 container
->AddChildView(gesture
);
398 MouseView
* mouse
= new MouseView
;
399 mouse
->SetBounds(30, 0, 30, 30);
400 container
->AddChildView(mouse
);
402 toplevel
->SetSize(gfx::Size(100, 100));
405 // Start a gesture on |gesture|.
406 ui::GestureEvent
tap_down(15,
410 ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN
));
411 ui::GestureEvent
end(15,
415 ui::GestureEventDetails(ui::ET_GESTURE_END
));
416 toplevel
->OnGestureEvent(&tap_down
);
418 // Now try to click on |mouse|. Since |gesture| will have capture, |mouse|
419 // will not receive the event.
420 gfx::Point
click_location(45, 15);
422 ui::MouseEvent
press(ui::ET_MOUSE_PRESSED
, click_location
, click_location
,
423 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON
,
424 ui::EF_LEFT_MOUSE_BUTTON
);
425 ui::MouseEvent
release(ui::ET_MOUSE_RELEASED
, click_location
, click_location
,
426 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON
,
427 ui::EF_LEFT_MOUSE_BUTTON
);
429 EXPECT_TRUE(toplevel
->HasCapture());
431 toplevel
->OnMouseEvent(&press
);
432 toplevel
->OnMouseEvent(&release
);
433 EXPECT_EQ(0, mouse
->pressed());
435 EXPECT_FALSE(toplevel
->HasCapture());
437 // The end of the gesture should release the capture, and pressing on |mouse|
438 // should now reach |mouse|.
439 toplevel
->OnGestureEvent(&end
);
440 toplevel
->OnMouseEvent(&press
);
441 toplevel
->OnMouseEvent(&release
);
442 EXPECT_EQ(1, mouse
->pressed());
445 RunPendingMessages();
448 // Checks that if a mouse-press triggers a capture on a different widget (which
449 // consumes the mouse-release event), then the target of the press does not have
451 TEST_F(WidgetTestInteractive
, DisableCaptureWidgetFromMousePress
) {
452 // The test creates two widgets: |first| and |second|.
453 // The View in |first| makes |second| visible, sets capture on it, and starts
454 // a nested loop (like a menu does). The View in |second| terminates the
455 // nested loop and closes the widget.
456 // The test sends a mouse-press event to |first|, and posts a task to send a
457 // release event to |second|, to make sure that the release event is
458 // dispatched after the nested loop starts.
460 Widget
* first
= CreateTopLevelFramelessPlatformWidget();
461 Widget
* second
= CreateTopLevelFramelessPlatformWidget();
463 View
* container
= new NestedLoopCaptureView(second
);
464 first
->SetContentsView(container
);
466 second
->SetContentsView(new ExitLoopOnRelease());
468 first
->SetSize(gfx::Size(100, 100));
471 gfx::Point
location(20, 20);
472 base::MessageLoop::current()->PostTask(
473 FROM_HERE
, base::Bind(&Widget::OnMouseEvent
, base::Unretained(second
),
474 base::Owned(new ui::MouseEvent(
475 ui::ET_MOUSE_RELEASED
, location
, location
,
476 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON
,
477 ui::EF_LEFT_MOUSE_BUTTON
))));
478 ui::MouseEvent
press(ui::ET_MOUSE_PRESSED
, location
, location
,
479 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON
,
480 ui::EF_LEFT_MOUSE_BUTTON
);
481 first
->OnMouseEvent(&press
);
482 EXPECT_FALSE(first
->HasCapture());
484 RunPendingMessages();
487 // Tests some grab/ungrab events.
488 // TODO(estade): can this be enabled now that this is an interactive ui test?
489 TEST_F(WidgetTestInteractive
, DISABLED_GrabUngrab
) {
490 Widget
* toplevel
= CreateTopLevelPlatformWidget();
491 Widget
* child1
= CreateChildNativeWidgetWithParent(toplevel
);
492 Widget
* child2
= CreateChildNativeWidgetWithParent(toplevel
);
494 toplevel
->SetBounds(gfx::Rect(0, 0, 500, 500));
496 child1
->SetBounds(gfx::Rect(10, 10, 300, 300));
497 View
* view
= new MouseView();
498 view
->SetBounds(0, 0, 300, 300);
499 child1
->GetRootView()->AddChildView(view
);
501 child2
->SetBounds(gfx::Rect(200, 10, 200, 200));
502 view
= new MouseView();
503 view
->SetBounds(0, 0, 200, 200);
504 child2
->GetRootView()->AddChildView(view
);
507 RunPendingMessages();
510 gfx::Point
p1(45, 45);
511 ui::MouseEvent
pressed(ui::ET_MOUSE_PRESSED
, p1
, p1
, ui::EventTimeForNow(),
512 ui::EF_LEFT_MOUSE_BUTTON
, ui::EF_LEFT_MOUSE_BUTTON
);
513 toplevel
->OnMouseEvent(&pressed
);
515 EXPECT_TRUE(toplevel
->HasCapture());
516 EXPECT_TRUE(child1
->HasCapture());
517 EXPECT_FALSE(child2
->HasCapture());
519 ui::MouseEvent
released(ui::ET_MOUSE_RELEASED
, p1
, p1
, ui::EventTimeForNow(),
520 ui::EF_LEFT_MOUSE_BUTTON
, ui::EF_LEFT_MOUSE_BUTTON
);
521 toplevel
->OnMouseEvent(&released
);
523 EXPECT_FALSE(toplevel
->HasCapture());
524 EXPECT_FALSE(child1
->HasCapture());
525 EXPECT_FALSE(child2
->HasCapture());
527 RunPendingMessages();
530 gfx::Point
p2(315, 45);
531 ui::MouseEvent
pressed2(ui::ET_MOUSE_PRESSED
, p2
, p2
, ui::EventTimeForNow(),
532 ui::EF_LEFT_MOUSE_BUTTON
, ui::EF_LEFT_MOUSE_BUTTON
);
533 toplevel
->OnMouseEvent(&pressed2
);
534 EXPECT_TRUE(pressed2
.handled());
535 EXPECT_TRUE(toplevel
->HasCapture());
536 EXPECT_TRUE(child2
->HasCapture());
537 EXPECT_FALSE(child1
->HasCapture());
539 ui::MouseEvent
released2(ui::ET_MOUSE_RELEASED
, p2
, p2
, ui::EventTimeForNow(),
540 ui::EF_LEFT_MOUSE_BUTTON
, ui::EF_LEFT_MOUSE_BUTTON
);
541 toplevel
->OnMouseEvent(&released2
);
542 EXPECT_FALSE(toplevel
->HasCapture());
543 EXPECT_FALSE(child1
->HasCapture());
544 EXPECT_FALSE(child2
->HasCapture());
546 toplevel
->CloseNow();
549 // Tests mouse move outside of the window into the "resize controller" and back
550 // will still generate an OnMouseEntered and OnMouseExited event..
551 TEST_F(WidgetTestInteractive
, CheckResizeControllerEvents
) {
552 Widget
* toplevel
= CreateTopLevelPlatformWidget();
554 toplevel
->SetBounds(gfx::Rect(0, 0, 100, 100));
556 MouseView
* view
= new MouseView();
557 view
->SetBounds(90, 90, 10, 10);
558 toplevel
->GetRootView()->AddChildView(view
);
561 RunPendingMessages();
563 // Move to an outside position.
564 gfx::Point
p1(200, 200);
565 ui::MouseEvent
moved_out(ui::ET_MOUSE_MOVED
, p1
, p1
, ui::EventTimeForNow(),
566 ui::EF_NONE
, ui::EF_NONE
);
567 toplevel
->OnMouseEvent(&moved_out
);
568 EXPECT_EQ(0, view
->EnteredCalls());
569 EXPECT_EQ(0, view
->ExitedCalls());
571 // Move onto the active view.
572 gfx::Point
p2(95, 95);
573 ui::MouseEvent
moved_over(ui::ET_MOUSE_MOVED
, p2
, p2
, ui::EventTimeForNow(),
574 ui::EF_NONE
, ui::EF_NONE
);
575 toplevel
->OnMouseEvent(&moved_over
);
576 EXPECT_EQ(1, view
->EnteredCalls());
577 EXPECT_EQ(0, view
->ExitedCalls());
579 // Move onto the outer resizing border.
580 gfx::Point
p3(102, 95);
581 ui::MouseEvent
moved_resizer(ui::ET_MOUSE_MOVED
, p3
, p3
,
582 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
583 toplevel
->OnMouseEvent(&moved_resizer
);
584 EXPECT_EQ(0, view
->EnteredCalls());
585 EXPECT_EQ(1, view
->ExitedCalls());
587 // Move onto the view again.
588 toplevel
->OnMouseEvent(&moved_over
);
589 EXPECT_EQ(1, view
->EnteredCalls());
590 EXPECT_EQ(0, view
->ExitedCalls());
592 RunPendingMessages();
594 toplevel
->CloseNow();
597 // Test view focus restoration when a widget is deactivated and re-activated.
598 TEST_F(WidgetTestInteractive
, ViewFocusOnWidgetActivationChanges
) {
599 Widget
* widget1
= CreateTopLevelPlatformWidget();
600 View
* view1
= new View
;
601 view1
->SetFocusable(true);
602 widget1
->GetContentsView()->AddChildView(view1
);
604 Widget
* widget2
= CreateTopLevelPlatformWidget();
605 View
* view2a
= new View
;
606 View
* view2b
= new View
;
607 view2a
->SetFocusable(true);
608 view2b
->SetFocusable(true);
609 widget2
->GetContentsView()->AddChildView(view2a
);
610 widget2
->GetContentsView()->AddChildView(view2b
);
613 EXPECT_TRUE(widget1
->IsActive());
614 view1
->RequestFocus();
615 EXPECT_EQ(view1
, widget1
->GetFocusManager()->GetFocusedView());
618 EXPECT_TRUE(widget2
->IsActive());
619 EXPECT_FALSE(widget1
->IsActive());
620 EXPECT_EQ(NULL
, widget1
->GetFocusManager()->GetFocusedView());
621 view2a
->RequestFocus();
622 EXPECT_EQ(view2a
, widget2
->GetFocusManager()->GetFocusedView());
623 view2b
->RequestFocus();
624 EXPECT_EQ(view2b
, widget2
->GetFocusManager()->GetFocusedView());
626 ActivateSync(widget1
);
627 EXPECT_TRUE(widget1
->IsActive());
628 EXPECT_EQ(view1
, widget1
->GetFocusManager()->GetFocusedView());
629 EXPECT_FALSE(widget2
->IsActive());
630 EXPECT_EQ(NULL
, widget2
->GetFocusManager()->GetFocusedView());
632 ActivateSync(widget2
);
633 EXPECT_TRUE(widget2
->IsActive());
634 EXPECT_EQ(view2b
, widget2
->GetFocusManager()->GetFocusedView());
635 EXPECT_FALSE(widget1
->IsActive());
636 EXPECT_EQ(NULL
, widget1
->GetFocusManager()->GetFocusedView());
644 // Test view focus retention when a widget's HWND is disabled and re-enabled.
645 TEST_F(WidgetTestInteractive
, ViewFocusOnHWNDEnabledChanges
) {
646 Widget
* widget
= CreateTopLevelFramelessPlatformWidget();
647 widget
->SetContentsView(new View
);
648 for (size_t i
= 0; i
< 2; ++i
) {
649 widget
->GetContentsView()->AddChildView(new View
);
650 widget
->GetContentsView()->child_at(i
)->SetFocusable(true);
654 const HWND hwnd
= HWNDForWidget(widget
);
655 EXPECT_TRUE(::IsWindow(hwnd
));
656 EXPECT_TRUE(::IsWindowEnabled(hwnd
));
657 EXPECT_EQ(hwnd
, ::GetActiveWindow());
659 for (int i
= 0; i
< widget
->GetContentsView()->child_count(); ++i
) {
660 SCOPED_TRACE(base::StringPrintf("Child view %d", i
));
661 View
* view
= widget
->GetContentsView()->child_at(i
);
663 view
->RequestFocus();
664 EXPECT_EQ(view
, widget
->GetFocusManager()->GetFocusedView());
665 EXPECT_FALSE(::EnableWindow(hwnd
, FALSE
));
666 EXPECT_FALSE(::IsWindowEnabled(hwnd
));
668 // Oddly, disabling the HWND leaves it active with the focus unchanged.
669 EXPECT_EQ(hwnd
, ::GetActiveWindow());
670 EXPECT_TRUE(widget
->IsActive());
671 EXPECT_EQ(view
, widget
->GetFocusManager()->GetFocusedView());
673 EXPECT_TRUE(::EnableWindow(hwnd
, TRUE
));
674 EXPECT_TRUE(::IsWindowEnabled(hwnd
));
675 EXPECT_EQ(hwnd
, ::GetActiveWindow());
676 EXPECT_TRUE(widget
->IsActive());
677 EXPECT_EQ(view
, widget
->GetFocusManager()->GetFocusedView());
683 // This class subclasses the Widget class to listen for activation change
684 // notifications and provides accessors to return information as to whether
685 // the widget is active. We need this to ensure that users of the widget
686 // class activate the widget only when the underlying window becomes really
687 // active. Previously we would activate the widget in the WM_NCACTIVATE
688 // message which is incorrect because APIs like FlashWindowEx flash the
689 // window caption by sending fake WM_NCACTIVATE messages.
690 class WidgetActivationTest
: public Widget
{
692 WidgetActivationTest()
695 ~WidgetActivationTest() override
{}
697 void OnNativeWidgetActivationChanged(bool active
) override
{
701 bool active() const { return active_
; }
706 DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest
);
709 // Tests whether the widget only becomes active when the underlying window
711 TEST_F(WidgetTestInteractive
, WidgetNotActivatedOnFakeActivationMessages
) {
712 WidgetActivationTest widget1
;
713 Widget::InitParams init_params
=
714 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
715 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
716 init_params
.native_widget
= new DesktopNativeWidgetAura(&widget1
);
717 init_params
.bounds
= gfx::Rect(0, 0, 200, 200);
718 widget1
.Init(init_params
);
720 EXPECT_EQ(true, widget1
.active());
722 WidgetActivationTest widget2
;
723 init_params
.native_widget
= new DesktopNativeWidgetAura(&widget2
);
724 widget2
.Init(init_params
);
726 EXPECT_EQ(true, widget2
.active());
727 EXPECT_EQ(false, widget1
.active());
729 HWND win32_native_window1
= HWNDForWidget(&widget1
);
730 EXPECT_TRUE(::IsWindow(win32_native_window1
));
732 ::SendMessage(win32_native_window1
, WM_NCACTIVATE
, 1, 0);
733 EXPECT_EQ(false, widget1
.active());
734 EXPECT_EQ(true, widget2
.active());
736 ::SetActiveWindow(win32_native_window1
);
737 EXPECT_EQ(true, widget1
.active());
738 EXPECT_EQ(false, widget2
.active());
740 #endif // defined(OS_WIN)
742 #if !defined(OS_CHROMEOS)
743 // Provides functionality to create a window modal dialog.
744 class ModalDialogDelegate
: public DialogDelegateView
{
746 explicit ModalDialogDelegate(ui::ModalType type
) : type_(type
) {}
747 ~ModalDialogDelegate() override
{}
749 // WidgetDelegate overrides.
750 ui::ModalType
GetModalType() const override
{ return type_
; }
755 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate
);
758 // Tests whether the focused window is set correctly when a modal window is
759 // created and destroyed. When it is destroyed it should focus the owner window.
760 TEST_F(WidgetTestInteractive
, WindowModalWindowDestroyedActivationTest
) {
761 TestWidgetFocusChangeListener focus_listener
;
762 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener
);
763 const std::vector
<gfx::NativeView
>& focus_changes
=
764 focus_listener
.focus_changes();
766 // Create a top level widget.
767 Widget top_level_widget
;
768 Widget::InitParams init_params
=
769 CreateParams(Widget::InitParams::TYPE_WINDOW
);
770 init_params
.show_state
= ui::SHOW_STATE_NORMAL
;
771 gfx::Rect
initial_bounds(0, 0, 500, 500);
772 init_params
.bounds
= initial_bounds
;
773 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
774 init_params
.native_widget
=
775 new PlatformDesktopNativeWidget(&top_level_widget
);
776 top_level_widget
.Init(init_params
);
777 ShowSync(&top_level_widget
);
779 gfx::NativeView top_level_native_view
= top_level_widget
.GetNativeView();
780 ASSERT_FALSE(focus_listener
.focus_changes().empty());
781 EXPECT_EQ(1u, focus_changes
.size());
782 EXPECT_EQ(top_level_native_view
, focus_changes
[0]);
784 // Create a modal dialog.
785 // This instance will be destroyed when the dialog is destroyed.
786 ModalDialogDelegate
* dialog_delegate
=
787 new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW
);
789 Widget
* modal_dialog_widget
= views::DialogDelegate::CreateDialogWidget(
790 dialog_delegate
, NULL
, top_level_widget
.GetNativeView());
791 modal_dialog_widget
->SetBounds(gfx::Rect(100, 100, 200, 200));
793 // Note the dialog widget doesn't need a ShowSync. Since it is modal, it gains
794 // active status synchronously, even on Mac.
795 modal_dialog_widget
->Show();
797 gfx::NativeView modal_native_view
= modal_dialog_widget
->GetNativeView();
798 EXPECT_EQ(3u, focus_changes
.size());
799 EXPECT_EQ(nullptr, focus_changes
[1]);
800 EXPECT_EQ(modal_native_view
, focus_changes
[2]);
802 #if defined(OS_MACOSX)
803 // Window modal dialogs on Mac are "sheets", which animate to close before
804 // activating their parent widget.
805 WidgetActivationWaiter
waiter(&top_level_widget
, true);
806 modal_dialog_widget
->Close();
809 modal_dialog_widget
->CloseNow();
812 EXPECT_EQ(5u, focus_changes
.size());
813 EXPECT_EQ(nullptr, focus_changes
[3]);
814 EXPECT_EQ(top_level_native_view
, focus_changes
[4]);
816 top_level_widget
.CloseNow();
817 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener
);
820 // Disabled on Mac. Desktop Mac doesn't have system modal windows since Carbon
821 // was deprecated. It does have application modal windows, but only Ash requests
823 #if defined(OS_MACOSX) && !defined(USE_AURA)
824 #define MAYBE_SystemModalWindowReleasesCapture \
825 DISABLED_SystemModalWindowReleasesCapture
827 #define MAYBE_SystemModalWindowReleasesCapture SystemModalWindowReleasesCapture
830 // Test that when opening a system-modal window, capture is released.
831 TEST_F(WidgetTestInteractive
, MAYBE_SystemModalWindowReleasesCapture
) {
832 TestWidgetFocusChangeListener focus_listener
;
833 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener
);
835 // Create a top level widget.
836 Widget top_level_widget
;
837 Widget::InitParams init_params
=
838 CreateParams(Widget::InitParams::TYPE_WINDOW
);
839 init_params
.show_state
= ui::SHOW_STATE_NORMAL
;
840 gfx::Rect
initial_bounds(0, 0, 500, 500);
841 init_params
.bounds
= initial_bounds
;
842 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
843 init_params
.native_widget
=
844 new PlatformDesktopNativeWidget(&top_level_widget
);
845 top_level_widget
.Init(init_params
);
846 ShowSync(&top_level_widget
);
848 ASSERT_FALSE(focus_listener
.focus_changes().empty());
849 EXPECT_EQ(top_level_widget
.GetNativeView(),
850 focus_listener
.focus_changes().back());;
852 EXPECT_FALSE(top_level_widget
.HasCapture());
853 top_level_widget
.SetCapture(NULL
);
854 EXPECT_TRUE(top_level_widget
.HasCapture());
856 // Create a modal dialog.
857 ModalDialogDelegate
* dialog_delegate
=
858 new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM
);
860 Widget
* modal_dialog_widget
= views::DialogDelegate::CreateDialogWidget(
861 dialog_delegate
, NULL
, top_level_widget
.GetNativeView());
862 modal_dialog_widget
->SetBounds(gfx::Rect(100, 100, 200, 200));
863 ShowSync(modal_dialog_widget
);
865 EXPECT_FALSE(top_level_widget
.HasCapture());
867 modal_dialog_widget
->CloseNow();
868 top_level_widget
.CloseNow();
869 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener
);
872 #endif // !defined(OS_CHROMEOS)
874 TEST_F(WidgetTestInteractive
, CanActivateFlagIsHonored
) {
876 Widget::InitParams init_params
=
877 CreateParams(Widget::InitParams::TYPE_WINDOW
);
878 init_params
.bounds
= gfx::Rect(0, 0, 200, 200);
879 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
880 init_params
.activatable
= Widget::InitParams::ACTIVATABLE_NO
;
881 #if !defined(OS_CHROMEOS)
882 init_params
.native_widget
= new PlatformDesktopNativeWidget(&widget
);
883 #endif // !defined(OS_CHROMEOS)
884 widget
.Init(init_params
);
887 EXPECT_FALSE(widget
.IsActive());
890 #if defined(USE_AURA)
891 // Test that touch selection quick menu is not activated when opened.
892 TEST_F(WidgetTestInteractive
, TouchSelectionQuickMenuIsNotActivated
) {
893 base::CommandLine::ForCurrentProcess()->AppendSwitch(
894 switches::kEnableTouchEditing
);
896 views_delegate()->set_use_desktop_native_widgets(true);
897 #endif // !defined(OS_WIN)
899 Widget
* widget
= CreateWidget();
901 Textfield
* textfield
= new Textfield
;
902 textfield
->SetBounds(0, 0, 200, 20);
903 textfield
->SetText(base::ASCIIToUTF16("some text"));
904 widget
->GetRootView()->AddChildView(textfield
);
907 textfield
->RequestFocus();
908 textfield
->SelectAll(true);
909 TextfieldTestApi
textfield_test_api(textfield
);
911 RunPendingMessages();
913 ui::test::EventGenerator
generator(widget
->GetNativeWindow());
914 generator
.GestureTapAt(gfx::Point(10, 10));
915 ShowQuickMenuImmediately(static_cast<TouchSelectionControllerImpl
*>(
916 textfield_test_api
.touch_selection_controller()));
918 EXPECT_TRUE(textfield
->HasFocus());
919 EXPECT_TRUE(widget
->IsActive());
920 EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning());
923 #endif // defined(USE_AURA)
925 TEST_F(WidgetTestInteractive
, DisableViewDoesNotActivateWidget
) {
927 views_delegate()->set_use_desktop_native_widgets(true);
928 #endif // !defined(OS_WIN)
930 // Create first widget and view, activate the widget, and focus the view.
932 Widget::InitParams params1
= CreateParams(Widget::InitParams::TYPE_POPUP
);
933 params1
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
934 params1
.activatable
= Widget::InitParams::ACTIVATABLE_YES
;
935 widget1
.Init(params1
);
937 View
* view1
= new View();
938 view1
->SetFocusable(true);
939 widget1
.GetRootView()->AddChildView(view1
);
941 ActivateSync(&widget1
);
943 FocusManager
* focus_manager1
= widget1
.GetFocusManager();
944 ASSERT_TRUE(focus_manager1
);
945 focus_manager1
->SetFocusedView(view1
);
946 EXPECT_EQ(view1
, focus_manager1
->GetFocusedView());
948 // Create second widget and view, activate the widget, and focus the view.
950 Widget::InitParams params2
= CreateParams(Widget::InitParams::TYPE_POPUP
);
951 params2
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
952 params2
.activatable
= Widget::InitParams::ACTIVATABLE_YES
;
953 widget2
.Init(params2
);
955 View
* view2
= new View();
956 view2
->SetFocusable(true);
957 widget2
.GetRootView()->AddChildView(view2
);
959 ActivateSync(&widget2
);
960 EXPECT_TRUE(widget2
.IsActive());
961 EXPECT_FALSE(widget1
.IsActive());
963 FocusManager
* focus_manager2
= widget2
.GetFocusManager();
964 ASSERT_TRUE(focus_manager2
);
965 focus_manager2
->SetFocusedView(view2
);
966 EXPECT_EQ(view2
, focus_manager2
->GetFocusedView());
968 // Disable the first view and make sure it loses focus, but its widget is not
970 view1
->SetEnabled(false);
971 EXPECT_NE(view1
, focus_manager1
->GetFocusedView());
972 EXPECT_FALSE(widget1
.IsActive());
973 EXPECT_TRUE(widget2
.IsActive());
976 TEST_F(WidgetTestInteractive
, ShowCreatesActiveWindow
) {
977 Widget
* widget
= CreateTopLevelPlatformWidget();
980 EXPECT_EQ(GetWidgetShowState(widget
), ui::SHOW_STATE_NORMAL
);
985 TEST_F(WidgetTestInteractive
, ShowInactive
) {
986 Widget
* widget
= CreateTopLevelPlatformWidget();
988 ShowInactiveSync(widget
);
989 EXPECT_EQ(GetWidgetShowState(widget
), ui::SHOW_STATE_INACTIVE
);
994 TEST_F(WidgetTestInteractive
, InactiveBeforeShow
) {
995 Widget
* widget
= CreateTopLevelPlatformWidget();
997 EXPECT_FALSE(widget
->IsActive());
998 EXPECT_FALSE(widget
->IsVisible());
1002 EXPECT_TRUE(widget
->IsActive());
1003 EXPECT_TRUE(widget
->IsVisible());
1008 TEST_F(WidgetTestInteractive
, ShowInactiveAfterShow
) {
1009 // Create 2 widgets to ensure window layering does not change.
1010 Widget
* widget
= CreateTopLevelPlatformWidget();
1011 Widget
* widget2
= CreateTopLevelPlatformWidget();
1014 EXPECT_FALSE(widget
->IsActive());
1015 EXPECT_TRUE(widget2
->IsVisible());
1016 EXPECT_TRUE(widget2
->IsActive());
1019 EXPECT_TRUE(widget
->IsActive());
1020 EXPECT_FALSE(widget2
->IsActive());
1022 ShowInactiveSync(widget
);
1023 EXPECT_TRUE(widget
->IsActive());
1024 EXPECT_FALSE(widget2
->IsActive());
1025 EXPECT_EQ(GetWidgetShowState(widget
), ui::SHOW_STATE_NORMAL
);
1027 widget2
->CloseNow();
1031 TEST_F(WidgetTestInteractive
, ShowAfterShowInactive
) {
1032 Widget
* widget
= CreateTopLevelPlatformWidget();
1033 widget
->SetBounds(gfx::Rect(100, 100, 100, 100));
1035 ShowInactiveSync(widget
);
1037 EXPECT_EQ(GetWidgetShowState(widget
), ui::SHOW_STATE_NORMAL
);
1042 #if !defined(OS_CHROMEOS)
1043 TEST_F(WidgetTestInteractive
, InactiveWidgetDoesNotGrabActivation
) {
1044 Widget
* widget
= CreateTopLevelPlatformWidget();
1046 EXPECT_EQ(GetWidgetShowState(widget
), ui::SHOW_STATE_NORMAL
);
1049 Widget::InitParams params
= CreateParams(Widget::InitParams::TYPE_POPUP
);
1050 params
.native_widget
= new PlatformDesktopNativeWidget(&widget2
);
1051 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1052 widget2
.Init(params
);
1054 RunPendingMessagesForActiveStatusChange();
1056 EXPECT_EQ(GetWidgetShowState(&widget2
), ui::SHOW_STATE_INACTIVE
);
1057 EXPECT_EQ(GetWidgetShowState(widget
), ui::SHOW_STATE_NORMAL
);
1062 #endif // !defined(OS_CHROMEOS)
1064 // ExitFullscreenRestoreState doesn't use DesktopAura widgets. On Mac, there are
1065 // currently only Desktop widgets and fullscreen changes have to coordinate with
1066 // the OS. See BridgedNativeWidgetUITest for native Mac fullscreen tests.
1067 // Maximize on mac is also (intentionally) a no-op.
1068 #if defined(OS_MACOSX) && !defined(USE_AURA)
1069 #define MAYBE_ExitFullscreenRestoreState DISABLED_ExitFullscreenRestoreState
1071 #define MAYBE_ExitFullscreenRestoreState ExitFullscreenRestoreState
1074 // Test that window state is not changed after getting out of full screen.
1075 TEST_F(WidgetTestInteractive
, MAYBE_ExitFullscreenRestoreState
) {
1076 Widget
* toplevel
= CreateTopLevelPlatformWidget();
1079 RunPendingMessages();
1081 // This should be a normal state window.
1082 EXPECT_EQ(ui::SHOW_STATE_NORMAL
, GetWidgetShowState(toplevel
));
1084 toplevel
->SetFullscreen(true);
1085 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN
, GetWidgetShowState(toplevel
));
1086 toplevel
->SetFullscreen(false);
1087 EXPECT_NE(ui::SHOW_STATE_FULLSCREEN
, GetWidgetShowState(toplevel
));
1089 // And it should still be in normal state after getting out of full screen.
1090 EXPECT_EQ(ui::SHOW_STATE_NORMAL
, GetWidgetShowState(toplevel
));
1092 // Now, make it maximized.
1093 toplevel
->Maximize();
1094 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED
, GetWidgetShowState(toplevel
));
1096 toplevel
->SetFullscreen(true);
1097 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN
, GetWidgetShowState(toplevel
));
1098 toplevel
->SetFullscreen(false);
1099 EXPECT_NE(ui::SHOW_STATE_FULLSCREEN
, GetWidgetShowState(toplevel
));
1101 // And it stays maximized after getting out of full screen.
1102 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED
, GetWidgetShowState(toplevel
));
1106 RunPendingMessages();
1111 // Used to veirfy OnMouseCaptureLost() has been invoked.
1112 class CaptureLostTrackingWidget
: public Widget
{
1114 CaptureLostTrackingWidget() : got_capture_lost_(false) {}
1115 ~CaptureLostTrackingWidget() override
{}
1117 bool GetAndClearGotCaptureLost() {
1118 bool value
= got_capture_lost_
;
1119 got_capture_lost_
= false;
1124 void OnMouseCaptureLost() override
{
1125 got_capture_lost_
= true;
1126 Widget::OnMouseCaptureLost();
1130 bool got_capture_lost_
;
1132 DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget
);
1137 class WidgetCaptureTest
: public ViewsTestBase
{
1139 WidgetCaptureTest() {
1142 ~WidgetCaptureTest() override
{}
1144 void SetUp() override
{
1145 gfx::GLSurfaceTestSupport::InitializeOneOff();
1146 ui::RegisterPathProvider();
1147 base::FilePath ui_test_pak_path
;
1148 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK
, &ui_test_pak_path
));
1149 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path
);
1150 ViewsTestBase::SetUp();
1153 // Verifies Widget::SetCapture() results in updating native capture along with
1154 // invoking the right Widget function.
1155 void TestCapture(bool use_desktop_native_widget
) {
1156 CaptureLostTrackingWidget widget1
;
1157 Widget::InitParams params1
=
1158 CreateParams(views::Widget::InitParams::TYPE_WINDOW
);
1159 params1
.native_widget
= CreateNativeWidget(use_desktop_native_widget
,
1161 params1
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1162 widget1
.Init(params1
);
1165 CaptureLostTrackingWidget widget2
;
1166 Widget::InitParams params2
=
1167 CreateParams(views::Widget::InitParams::TYPE_WINDOW
);
1168 params2
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1169 params2
.native_widget
= CreateNativeWidget(use_desktop_native_widget
,
1171 widget2
.Init(params2
);
1174 // Set capture to widget2 and verity it gets it.
1175 widget2
.SetCapture(widget2
.GetRootView());
1176 EXPECT_FALSE(widget1
.HasCapture());
1177 EXPECT_TRUE(widget2
.HasCapture());
1178 EXPECT_FALSE(widget1
.GetAndClearGotCaptureLost());
1179 EXPECT_FALSE(widget2
.GetAndClearGotCaptureLost());
1181 // Set capture to widget1 and verify it gets it.
1182 widget1
.SetCapture(widget1
.GetRootView());
1183 EXPECT_TRUE(widget1
.HasCapture());
1184 EXPECT_FALSE(widget2
.HasCapture());
1185 EXPECT_FALSE(widget1
.GetAndClearGotCaptureLost());
1186 EXPECT_TRUE(widget2
.GetAndClearGotCaptureLost());
1188 // Release and verify no one has it.
1189 widget1
.ReleaseCapture();
1190 EXPECT_FALSE(widget1
.HasCapture());
1191 EXPECT_FALSE(widget2
.HasCapture());
1192 EXPECT_TRUE(widget1
.GetAndClearGotCaptureLost());
1193 EXPECT_FALSE(widget2
.GetAndClearGotCaptureLost());
1196 NativeWidget
* CreateNativeWidget(bool create_desktop_native_widget
,
1198 #if !defined(OS_CHROMEOS)
1199 if (create_desktop_native_widget
)
1200 return new PlatformDesktopNativeWidget(widget
);
1206 DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest
);
1209 // See description in TestCapture().
1210 TEST_F(WidgetCaptureTest
, Capture
) {
1214 #if !defined(OS_CHROMEOS)
1215 // See description in TestCapture(). Creates DesktopNativeWidget.
1216 TEST_F(WidgetCaptureTest
, CaptureDesktopNativeWidget
) {
1221 // Test that no state is set if capture fails.
1222 TEST_F(WidgetCaptureTest
, FailedCaptureRequestIsNoop
) {
1224 Widget::InitParams params
=
1225 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
1226 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1227 params
.bounds
= gfx::Rect(400, 400);
1228 widget
.Init(params
);
1230 MouseView
* mouse_view1
= new MouseView
;
1231 MouseView
* mouse_view2
= new MouseView
;
1232 View
* contents_view
= new View
;
1233 contents_view
->AddChildView(mouse_view1
);
1234 contents_view
->AddChildView(mouse_view2
);
1235 widget
.SetContentsView(contents_view
);
1237 mouse_view1
->SetBounds(0, 0, 200, 400);
1238 mouse_view2
->SetBounds(200, 0, 200, 400);
1240 // Setting capture should fail because |widget| is not visible.
1241 widget
.SetCapture(mouse_view1
);
1242 EXPECT_FALSE(widget
.HasCapture());
1245 ui::test::EventGenerator
generator(GetContext(), widget
.GetNativeWindow());
1246 generator
.set_current_location(gfx::Point(300, 10));
1247 generator
.PressLeftButton();
1249 EXPECT_FALSE(mouse_view1
->pressed());
1250 EXPECT_TRUE(mouse_view2
->pressed());
1253 // Regression test for http://crbug.com/382421 (Linux-Aura issue).
1254 // TODO(pkotwicz): Make test pass on CrOS and Windows.
1255 // TODO(tapted): Investigate for toolkit-views on Mac http;//crbug.com/441064.
1256 #if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_MACOSX)
1257 #define MAYBE_MouseExitOnCaptureGrab DISABLED_MouseExitOnCaptureGrab
1259 #define MAYBE_MouseExitOnCaptureGrab MouseExitOnCaptureGrab
1262 // Test that a synthetic mouse exit is sent to the widget which was handling
1263 // mouse events when a different widget grabs capture.
1264 TEST_F(WidgetCaptureTest
, MAYBE_MouseExitOnCaptureGrab
) {
1266 Widget::InitParams params1
=
1267 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
1268 params1
.native_widget
= CreateNativeWidget(true, &widget1
);
1269 params1
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1270 widget1
.Init(params1
);
1271 MouseView
* mouse_view1
= new MouseView
;
1272 widget1
.SetContentsView(mouse_view1
);
1274 widget1
.SetBounds(gfx::Rect(300, 300));
1277 Widget::InitParams params2
=
1278 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
1279 params2
.native_widget
= CreateNativeWidget(true, &widget2
);
1280 params2
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1281 widget2
.Init(params2
);
1283 widget2
.SetBounds(gfx::Rect(400, 0, 300, 300));
1285 ui::test::EventGenerator
generator(widget1
.GetNativeWindow());
1286 generator
.set_current_location(gfx::Point(100, 100));
1287 generator
.MoveMouseBy(0, 0);
1289 EXPECT_EQ(1, mouse_view1
->EnteredCalls());
1290 EXPECT_EQ(0, mouse_view1
->ExitedCalls());
1292 widget2
.SetCapture(NULL
);
1293 EXPECT_EQ(0, mouse_view1
->EnteredCalls());
1294 // Grabbing native capture on Windows generates a ui::ET_MOUSE_EXITED event
1295 // in addition to the one generated by Chrome.
1296 EXPECT_LT(0, mouse_view1
->ExitedCalls());
1301 // Widget observer which grabs capture when the widget is activated.
1302 class CaptureOnActivationObserver
: public WidgetObserver
{
1304 CaptureOnActivationObserver() : activation_observed_(false) {}
1305 ~CaptureOnActivationObserver() override
{}
1308 void OnWidgetActivationChanged(Widget
* widget
, bool active
) override
{
1310 widget
->SetCapture(nullptr);
1311 activation_observed_
= true;
1315 bool activation_observed() const { return activation_observed_
; }
1318 bool activation_observed_
;
1320 DISALLOW_COPY_AND_ASSIGN(CaptureOnActivationObserver
);
1325 // Test that setting capture on widget activation of a non-toplevel widget
1326 // (e.g. a bubble on Linux) succeeds.
1327 TEST_F(WidgetCaptureTest
, SetCaptureToNonToplevel
) {
1329 Widget::InitParams toplevel_params
=
1330 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
1331 toplevel_params
.native_widget
= CreateNativeWidget(true, &toplevel
);
1332 toplevel_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1333 toplevel
.Init(toplevel_params
);
1336 Widget
* child
= new Widget
;
1337 Widget::InitParams child_params
=
1338 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
1339 child_params
.parent
= toplevel
.GetNativeView();
1340 child_params
.context
= toplevel
.GetNativeWindow();
1341 child
->Init(child_params
);
1343 CaptureOnActivationObserver observer
;
1344 child
->AddObserver(&observer
);
1347 #if defined(OS_MACOSX) && !defined(USE_AURA)
1348 // On Mac, activation is asynchronous. A single trip to the runloop should be
1349 // sufficient. On Aura platforms, note that since the child widget isn't top-
1350 // level, the aura window manager gets asked whether the widget is active, not
1352 base::RunLoop().RunUntilIdle();
1355 EXPECT_TRUE(observer
.activation_observed());
1356 EXPECT_TRUE(child
->HasCapture());
1363 // Used to verify OnMouseEvent() has been invoked.
1364 class MouseEventTrackingWidget
: public Widget
{
1366 MouseEventTrackingWidget() : got_mouse_event_(false) {}
1367 ~MouseEventTrackingWidget() override
{}
1369 bool GetAndClearGotMouseEvent() {
1370 bool value
= got_mouse_event_
;
1371 got_mouse_event_
= false;
1376 void OnMouseEvent(ui::MouseEvent
* event
) override
{
1377 got_mouse_event_
= true;
1378 Widget::OnMouseEvent(event
);
1382 bool got_mouse_event_
;
1384 DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget
);
1389 // Verifies if a mouse event is received on a widget that doesn't have capture
1390 // on Windows that it is correctly processed by the widget that doesn't have
1391 // capture. This behavior is not desired on OSes other than Windows.
1392 TEST_F(WidgetCaptureTest
, MouseEventDispatchedToRightWindow
) {
1393 MouseEventTrackingWidget widget1
;
1394 Widget::InitParams params1
=
1395 CreateParams(views::Widget::InitParams::TYPE_WINDOW
);
1396 params1
.native_widget
= new DesktopNativeWidgetAura(&widget1
);
1397 params1
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1398 widget1
.Init(params1
);
1401 MouseEventTrackingWidget widget2
;
1402 Widget::InitParams params2
=
1403 CreateParams(views::Widget::InitParams::TYPE_WINDOW
);
1404 params2
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
1405 params2
.native_widget
= new DesktopNativeWidgetAura(&widget2
);
1406 widget2
.Init(params2
);
1409 // Set capture to widget2 and verity it gets it.
1410 widget2
.SetCapture(widget2
.GetRootView());
1411 EXPECT_FALSE(widget1
.HasCapture());
1412 EXPECT_TRUE(widget2
.HasCapture());
1414 widget1
.GetAndClearGotMouseEvent();
1415 widget2
.GetAndClearGotMouseEvent();
1416 // Send a mouse event to the RootWindow associated with |widget1|. Even though
1417 // |widget2| has capture, |widget1| should still get the event.
1418 ui::MouseEvent
mouse_event(ui::ET_MOUSE_EXITED
, gfx::Point(), gfx::Point(),
1419 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
1420 ui::EventDispatchDetails details
= widget1
.GetNativeWindow()->
1421 GetHost()->event_processor()->OnEventFromSource(&mouse_event
);
1422 ASSERT_FALSE(details
.dispatcher_destroyed
);
1423 EXPECT_TRUE(widget1
.GetAndClearGotMouseEvent());
1424 EXPECT_FALSE(widget2
.GetAndClearGotMouseEvent());
1426 #endif // defined(OS_WIN)
1428 class WidgetInputMethodInteractiveTest
: public WidgetTestInteractive
{
1430 WidgetInputMethodInteractiveTest() {}
1433 void SetUp() override
{
1434 WidgetTestInteractive::SetUp();
1436 // On Windows, Widget::Deactivate() works by activating the next topmost
1437 // window on the z-order stack. This only works if there is at least one
1438 // other window, so make sure that is the case.
1439 deactivate_widget_
= CreateWidget();
1440 deactivate_widget_
->Show();
1444 void TearDown() override
{
1445 if (deactivate_widget_
)
1446 deactivate_widget_
->CloseNow();
1447 WidgetTestInteractive::TearDown();
1451 Widget
* deactivate_widget_
= nullptr;
1453 DISALLOW_COPY_AND_ASSIGN(WidgetInputMethodInteractiveTest
);
1456 // Test input method focus changes affected by top window activaction.
1457 TEST_F(WidgetInputMethodInteractiveTest
, Activation
) {
1458 Widget
* widget
= CreateWidget();
1459 Textfield
* textfield
= new Textfield
;
1460 widget
->GetRootView()->AddChildView(textfield
);
1461 textfield
->RequestFocus();
1465 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT
,
1466 widget
->GetInputMethod()->GetTextInputType());
1468 DeactivateSync(widget
);
1470 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE
,
1471 widget
->GetInputMethod()->GetTextInputType());
1475 // Test input method focus changes affected by focus changes within 1 window.
1476 TEST_F(WidgetInputMethodInteractiveTest
, OneWindow
) {
1477 Widget
* widget
= CreateWidget();
1478 Textfield
* textfield1
= new Textfield
;
1479 Textfield
* textfield2
= new Textfield
;
1480 textfield2
->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD
);
1481 widget
->GetRootView()->AddChildView(textfield1
);
1482 widget
->GetRootView()->AddChildView(textfield2
);
1486 textfield1
->RequestFocus();
1487 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT
,
1488 widget
->GetInputMethod()->GetTextInputType());
1490 textfield2
->RequestFocus();
1491 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD
,
1492 widget
->GetInputMethod()->GetTextInputType());
1494 // Widget::Deactivate() doesn't work for CrOS, because it uses NWA instead of
1495 // DNWA (which just activates the last active window) and involves the
1496 // AuraTestHelper which sets the input method as DummyInputMethod.
1497 #if !defined(OS_CHROMEOS)
1498 DeactivateSync(widget
);
1499 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE
,
1500 widget
->GetInputMethod()->GetTextInputType());
1502 ActivateSync(widget
);
1503 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD
,
1504 widget
->GetInputMethod()->GetTextInputType());
1506 DeactivateSync(widget
);
1507 textfield1
->RequestFocus();
1508 ActivateSync(widget
);
1509 EXPECT_TRUE(widget
->IsActive());
1510 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT
,
1511 widget
->GetInputMethod()->GetTextInputType());
1516 // Test input method focus changes affected by focus changes cross 2 windows
1517 // which shares the same top window.
1518 TEST_F(WidgetInputMethodInteractiveTest
, TwoWindows
) {
1519 Widget
* parent
= CreateWidget();
1520 parent
->SetBounds(gfx::Rect(100, 100, 100, 100));
1522 Widget
* child
= CreateChildNativeWidgetWithParent(parent
);
1523 child
->SetBounds(gfx::Rect(0, 0, 50, 50));
1526 Textfield
* textfield_parent
= new Textfield
;
1527 Textfield
* textfield_child
= new Textfield
;
1528 textfield_parent
->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD
);
1529 parent
->GetRootView()->AddChildView(textfield_parent
);
1530 child
->GetRootView()->AddChildView(textfield_child
);
1533 EXPECT_EQ(parent
->GetInputMethod(), child
->GetInputMethod());
1535 textfield_parent
->RequestFocus();
1536 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD
,
1537 parent
->GetInputMethod()->GetTextInputType());
1539 textfield_child
->RequestFocus();
1540 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT
,
1541 parent
->GetInputMethod()->GetTextInputType());
1543 // Widget::Deactivate() doesn't work for CrOS, because it uses NWA instead of
1544 // DNWA (which just activates the last active window) and involves the
1545 // AuraTestHelper which sets the input method as DummyInputMethod.
1546 #if !defined(OS_CHROMEOS)
1547 DeactivateSync(parent
);
1548 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE
,
1549 parent
->GetInputMethod()->GetTextInputType());
1551 ActivateSync(parent
);
1552 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT
,
1553 parent
->GetInputMethod()->GetTextInputType());
1555 textfield_parent
->RequestFocus();
1556 DeactivateSync(parent
);
1557 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE
,
1558 parent
->GetInputMethod()->GetTextInputType());
1560 ActivateSync(parent
);
1561 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD
,
1562 parent
->GetInputMethod()->GetTextInputType());
1568 // Test input method focus changes affected by textfield's state changes.
1569 TEST_F(WidgetInputMethodInteractiveTest
, TextField
) {
1570 Widget
* widget
= CreateWidget();
1571 Textfield
* textfield
= new Textfield
;
1572 widget
->GetRootView()->AddChildView(textfield
);
1574 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE
,
1575 widget
->GetInputMethod()->GetTextInputType());
1577 textfield
->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD
);
1578 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE
,
1579 widget
->GetInputMethod()->GetTextInputType());
1581 textfield
->RequestFocus();
1582 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD
,
1583 widget
->GetInputMethod()->GetTextInputType());
1585 textfield
->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT
);
1586 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT
,
1587 widget
->GetInputMethod()->GetTextInputType());
1589 textfield
->SetReadOnly(true);
1590 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE
,
1591 widget
->GetInputMethod()->GetTextInputType());
1596 } // namespace views