1 // Copyright 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 "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
8 #include "ui/aura/client/aura_constants.h"
9 #include "ui/aura/client/cursor_client.h"
10 #include "ui/aura/client/window_tree_client.h"
11 #include "ui/aura/test/test_window_delegate.h"
12 #include "ui/aura/window.h"
13 #include "ui/aura/window_tree_host.h"
14 #include "ui/events/event_processor.h"
15 #include "ui/events/event_utils.h"
16 #include "ui/events/test/event_generator.h"
17 #include "ui/gfx/screen.h"
18 #include "ui/views/test/test_views.h"
19 #include "ui/views/test/test_views_delegate.h"
20 #include "ui/views/test/views_test_base.h"
21 #include "ui/views/test/widget_test.h"
22 #include "ui/views/widget/widget.h"
23 #include "ui/views/window/dialog_delegate.h"
24 #include "ui/wm/public/dispatcher_client.h"
27 #include "ui/views/win/hwnd_util.h"
33 typedef ViewsTestBase DesktopNativeWidgetAuraTest
;
35 // Verifies creating a Widget with a parent that is not in a RootWindow doesn't
37 TEST_F(DesktopNativeWidgetAuraTest
, CreateWithParentNotInRootWindow
) {
38 scoped_ptr
<aura::Window
> window(new aura::Window(NULL
));
39 window
->Init(ui::LAYER_NOT_DRAWN
);
41 Widget::InitParams params
= CreateParams(Widget::InitParams::TYPE_WINDOW
);
42 params
.bounds
= gfx::Rect(0, 0, 200, 200);
43 params
.parent
= window
.get();
44 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
45 params
.native_widget
= new DesktopNativeWidgetAura(&widget
);
49 // Verifies that the Aura windows making up a widget instance have the correct
50 // bounds after the widget is resized.
51 TEST_F(DesktopNativeWidgetAuraTest
, DesktopAuraWindowSizeTest
) {
54 // On Linux we test this with popup windows because the WM may ignore the size
55 // suggestion for normal windows.
57 Widget::InitParams init_params
=
58 CreateParams(Widget::InitParams::TYPE_POPUP
);
60 Widget::InitParams init_params
=
61 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
64 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
65 init_params
.native_widget
= new DesktopNativeWidgetAura(&widget
);
66 widget
.Init(init_params
);
68 gfx::Rect
bounds(0, 0, 100, 100);
69 widget
.SetBounds(bounds
);
72 EXPECT_EQ(bounds
.ToString(),
73 widget
.GetNativeView()->GetRootWindow()->bounds().ToString());
74 EXPECT_EQ(bounds
.ToString(), widget
.GetNativeView()->bounds().ToString());
75 EXPECT_EQ(bounds
.ToString(),
76 widget
.GetNativeView()->parent()->bounds().ToString());
78 gfx::Rect
new_bounds(0, 0, 200, 200);
79 widget
.SetBounds(new_bounds
);
80 EXPECT_EQ(new_bounds
.ToString(),
81 widget
.GetNativeView()->GetRootWindow()->bounds().ToString());
82 EXPECT_EQ(new_bounds
.ToString(), widget
.GetNativeView()->bounds().ToString());
83 EXPECT_EQ(new_bounds
.ToString(),
84 widget
.GetNativeView()->parent()->bounds().ToString());
87 // Verifies GetNativeView() is initially hidden. If the native view is initially
88 // shown then animations can not be disabled.
89 TEST_F(DesktopNativeWidgetAuraTest
, NativeViewInitiallyHidden
) {
91 Widget::InitParams init_params
=
92 CreateParams(Widget::InitParams::TYPE_WINDOW
);
93 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
94 init_params
.native_widget
= new DesktopNativeWidgetAura(&widget
);
95 widget
.Init(init_params
);
96 EXPECT_FALSE(widget
.GetNativeView()->IsVisible());
99 // Verify that the cursor state is shared between two native widgets.
100 TEST_F(DesktopNativeWidgetAuraTest
, GlobalCursorState
) {
101 // Create two native widgets, each owning different root windows.
103 Widget::InitParams init_params_a
=
104 CreateParams(Widget::InitParams::TYPE_WINDOW
);
105 init_params_a
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
106 DesktopNativeWidgetAura
* desktop_native_widget_aura_a
=
107 new DesktopNativeWidgetAura(&widget_a
);
108 init_params_a
.native_widget
= desktop_native_widget_aura_a
;
109 widget_a
.Init(init_params_a
);
112 Widget::InitParams init_params_b
=
113 CreateParams(Widget::InitParams::TYPE_WINDOW
);
114 init_params_b
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
115 DesktopNativeWidgetAura
* desktop_native_widget_aura_b
=
116 new DesktopNativeWidgetAura(&widget_b
);
117 init_params_b
.native_widget
= desktop_native_widget_aura_b
;
118 widget_b
.Init(init_params_b
);
120 aura::client::CursorClient
* cursor_client_a
= aura::client::GetCursorClient(
121 desktop_native_widget_aura_a
->host()->window());
122 aura::client::CursorClient
* cursor_client_b
= aura::client::GetCursorClient(
123 desktop_native_widget_aura_b
->host()->window());
125 // Verify the cursor can be locked using one client and unlocked using
127 EXPECT_FALSE(cursor_client_a
->IsCursorLocked());
128 EXPECT_FALSE(cursor_client_b
->IsCursorLocked());
130 cursor_client_a
->LockCursor();
131 EXPECT_TRUE(cursor_client_a
->IsCursorLocked());
132 EXPECT_TRUE(cursor_client_b
->IsCursorLocked());
134 cursor_client_b
->UnlockCursor();
135 EXPECT_FALSE(cursor_client_a
->IsCursorLocked());
136 EXPECT_FALSE(cursor_client_b
->IsCursorLocked());
138 // Verify that mouse events can be disabled using one client and then
139 // re-enabled using another. Note that disabling mouse events should also
140 // have the side effect of making the cursor invisible.
141 EXPECT_TRUE(cursor_client_a
->IsCursorVisible());
142 EXPECT_TRUE(cursor_client_b
->IsCursorVisible());
143 EXPECT_TRUE(cursor_client_a
->IsMouseEventsEnabled());
144 EXPECT_TRUE(cursor_client_b
->IsMouseEventsEnabled());
146 cursor_client_b
->DisableMouseEvents();
147 EXPECT_FALSE(cursor_client_a
->IsCursorVisible());
148 EXPECT_FALSE(cursor_client_b
->IsCursorVisible());
149 EXPECT_FALSE(cursor_client_a
->IsMouseEventsEnabled());
150 EXPECT_FALSE(cursor_client_b
->IsMouseEventsEnabled());
152 cursor_client_a
->EnableMouseEvents();
153 EXPECT_TRUE(cursor_client_a
->IsCursorVisible());
154 EXPECT_TRUE(cursor_client_b
->IsCursorVisible());
155 EXPECT_TRUE(cursor_client_a
->IsMouseEventsEnabled());
156 EXPECT_TRUE(cursor_client_b
->IsMouseEventsEnabled());
158 // Verify that setting the cursor using one cursor client
159 // will set it for all root windows.
160 EXPECT_EQ(ui::kCursorNone
, cursor_client_a
->GetCursor().native_type());
161 EXPECT_EQ(ui::kCursorNone
, cursor_client_b
->GetCursor().native_type());
163 cursor_client_b
->SetCursor(ui::kCursorPointer
);
164 EXPECT_EQ(ui::kCursorPointer
, cursor_client_a
->GetCursor().native_type());
165 EXPECT_EQ(ui::kCursorPointer
, cursor_client_b
->GetCursor().native_type());
167 // Verify that hiding the cursor using one cursor client will
168 // hide it for all root windows. Note that hiding the cursor
169 // should not disable mouse events.
170 cursor_client_a
->HideCursor();
171 EXPECT_FALSE(cursor_client_a
->IsCursorVisible());
172 EXPECT_FALSE(cursor_client_b
->IsCursorVisible());
173 EXPECT_TRUE(cursor_client_a
->IsMouseEventsEnabled());
174 EXPECT_TRUE(cursor_client_b
->IsMouseEventsEnabled());
176 // Verify that the visibility state cannot be changed using one
177 // cursor client when the cursor was locked using another.
178 cursor_client_b
->LockCursor();
179 cursor_client_a
->ShowCursor();
180 EXPECT_FALSE(cursor_client_a
->IsCursorVisible());
181 EXPECT_FALSE(cursor_client_b
->IsCursorVisible());
183 // Verify the cursor becomes visible on unlock (since a request
184 // to make it visible was queued up while the cursor was locked).
185 cursor_client_b
->UnlockCursor();
186 EXPECT_TRUE(cursor_client_a
->IsCursorVisible());
187 EXPECT_TRUE(cursor_client_b
->IsCursorVisible());
190 // Verifies FocusController doesn't attempt to access |content_window_| during
191 // destruction. Previously the FocusController was destroyed after the window.
192 // This could be problematic as FocusController references |content_window_| and
193 // could attempt to use it after |content_window_| was destroyed. This test
194 // verifies this doesn't happen. Note that this test only failed under ASAN.
195 TEST_F(DesktopNativeWidgetAuraTest
, DontAccessContentWindowDuringDestruction
) {
196 aura::test::TestWindowDelegate delegate
;
199 Widget::InitParams init_params
=
200 CreateParams(Widget::InitParams::TYPE_WINDOW
);
201 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
202 DesktopNativeWidgetAura
* desktop_native_widget_aura
=
203 new DesktopNativeWidgetAura(&widget
);
204 init_params
.native_widget
= desktop_native_widget_aura
;
205 widget
.Init(init_params
);
207 // Owned by |widget|.
208 aura::Window
* window
= new aura::Window(&delegate
);
209 window
->Init(ui::LAYER_NOT_DRAWN
);
211 widget
.GetNativeWindow()->parent()->AddChild(window
);
217 void QuitNestedLoopAndCloseWidget(scoped_ptr
<Widget
> widget
,
218 base::Closure
* quit_runloop
) {
222 // Verifies that a widget can be destroyed when running a nested message-loop.
223 TEST_F(DesktopNativeWidgetAuraTest
, WidgetCanBeDestroyedFromNestedLoop
) {
224 scoped_ptr
<Widget
> widget(new Widget
);
225 Widget::InitParams params
= CreateParams(Widget::InitParams::TYPE_WINDOW
);
226 params
.bounds
= gfx::Rect(0, 0, 200, 200);
227 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
228 params
.native_widget
= new DesktopNativeWidgetAura(widget
.get());
229 widget
->Init(params
);
232 aura::Window
* window
= widget
->GetNativeView();
233 aura::Window
* root
= window
->GetRootWindow();
234 aura::client::DispatcherClient
* client
=
235 aura::client::GetDispatcherClient(root
);
237 // Post a task that terminates the nested loop and destroyes the widget. This
238 // task will be executed from the nested loop initiated with the call to
239 // |RunWithDispatcher()| below.
240 aura::client::DispatcherRunLoop
run_loop(client
, NULL
);
241 base::Closure quit_runloop
= run_loop
.QuitClosure();
242 message_loop()->PostTask(FROM_HERE
,
243 base::Bind(&QuitNestedLoopAndCloseWidget
,
244 base::Passed(&widget
),
245 base::Unretained(&quit_runloop
)));
249 // This class provides functionality to create fullscreen and top level popup
250 // windows. It additionally tests whether the destruction of these windows
251 // occurs correctly in desktop AURA without crashing.
252 // It provides facilities to test the following cases:-
253 // 1. Child window destroyed which should lead to the destruction of the
255 // 2. Parent window destroyed which should lead to the child being destroyed.
256 class DesktopAuraTopLevelWindowTest
: public aura::WindowObserver
{
258 DesktopAuraTopLevelWindowTest()
259 : top_level_widget_(NULL
),
261 owner_destroyed_(false),
262 owned_window_destroyed_(false),
263 use_async_mode_(true) {}
265 ~DesktopAuraTopLevelWindowTest() override
{
266 EXPECT_TRUE(owner_destroyed_
);
267 EXPECT_TRUE(owned_window_destroyed_
);
268 top_level_widget_
= NULL
;
269 owned_window_
= NULL
;
272 void CreateTopLevelWindow(const gfx::Rect
& bounds
, bool fullscreen
) {
273 Widget::InitParams init_params
;
274 init_params
.type
= Widget::InitParams::TYPE_WINDOW
;
275 init_params
.bounds
= bounds
;
276 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
277 init_params
.layer_type
= ui::LAYER_NOT_DRAWN
;
278 init_params
.accept_events
= fullscreen
;
280 widget_
.Init(init_params
);
282 owned_window_
= new aura::Window(&child_window_delegate_
);
283 owned_window_
->SetType(ui::wm::WINDOW_TYPE_NORMAL
);
284 owned_window_
->SetName("TestTopLevelWindow");
286 owned_window_
->SetProperty(aura::client::kShowStateKey
,
287 ui::SHOW_STATE_FULLSCREEN
);
289 owned_window_
->SetType(ui::wm::WINDOW_TYPE_MENU
);
291 owned_window_
->Init(ui::LAYER_TEXTURED
);
292 aura::client::ParentWindowWithContext(
294 widget_
.GetNativeView()->GetRootWindow(),
295 gfx::Rect(0, 0, 1900, 1600));
296 owned_window_
->Show();
297 owned_window_
->AddObserver(this);
299 ASSERT_TRUE(owned_window_
->parent() != NULL
);
300 owned_window_
->parent()->AddObserver(this);
303 views::Widget::GetWidgetForNativeView(owned_window_
->parent());
304 ASSERT_TRUE(top_level_widget_
!= NULL
);
307 void DestroyOwnedWindow() {
308 ASSERT_TRUE(owned_window_
!= NULL
);
309 // If async mode is off then clean up state here.
310 if (!use_async_mode_
) {
311 owned_window_
->RemoveObserver(this);
312 owned_window_
->parent()->RemoveObserver(this);
313 owner_destroyed_
= true;
314 owned_window_destroyed_
= true;
316 delete owned_window_
;
319 void DestroyOwnerWindow() {
320 ASSERT_TRUE(top_level_widget_
!= NULL
);
321 top_level_widget_
->CloseNow();
324 void OnWindowDestroying(aura::Window
* window
) override
{
325 window
->RemoveObserver(this);
326 if (window
== owned_window_
) {
327 owned_window_destroyed_
= true;
328 } else if (window
== top_level_widget_
->GetNativeView()) {
329 owner_destroyed_
= true;
331 ADD_FAILURE() << "Unexpected window destroyed callback: " << window
;
335 aura::Window
* owned_window() {
336 return owned_window_
;
339 views::Widget
* top_level_widget() {
340 return top_level_widget_
;
343 void set_use_async_mode(bool async_mode
) {
344 use_async_mode_
= async_mode
;
348 views::Widget widget_
;
349 views::Widget
* top_level_widget_
;
350 aura::Window
* owned_window_
;
351 bool owner_destroyed_
;
352 bool owned_window_destroyed_
;
353 aura::test::TestWindowDelegate child_window_delegate_
;
354 // This flag controls whether we need to wait for the destruction to complete
355 // before finishing the test. Defaults to true.
356 bool use_async_mode_
;
358 DISALLOW_COPY_AND_ASSIGN(DesktopAuraTopLevelWindowTest
);
361 class DesktopAuraWidgetTest
: public WidgetTest
{
363 DesktopAuraWidgetTest() {}
365 void SetUp() override
{
366 ViewsTestBase::SetUp();
367 views_delegate()->set_use_desktop_native_widgets(true);
371 DISALLOW_COPY_AND_ASSIGN(DesktopAuraWidgetTest
);
374 TEST_F(DesktopAuraWidgetTest
, FullscreenWindowDestroyedBeforeOwnerTest
) {
375 DesktopAuraTopLevelWindowTest fullscreen_window
;
376 ASSERT_NO_FATAL_FAILURE(fullscreen_window
.CreateTopLevelWindow(
377 gfx::Rect(0, 0, 200, 200), true));
379 RunPendingMessages();
380 ASSERT_NO_FATAL_FAILURE(fullscreen_window
.DestroyOwnedWindow());
381 RunPendingMessages();
384 TEST_F(DesktopAuraWidgetTest
, FullscreenWindowOwnerDestroyed
) {
385 DesktopAuraTopLevelWindowTest fullscreen_window
;
386 ASSERT_NO_FATAL_FAILURE(fullscreen_window
.CreateTopLevelWindow(
387 gfx::Rect(0, 0, 200, 200), true));
389 RunPendingMessages();
390 ASSERT_NO_FATAL_FAILURE(fullscreen_window
.DestroyOwnerWindow());
391 RunPendingMessages();
394 TEST_F(DesktopAuraWidgetTest
, TopLevelOwnedPopupTest
) {
395 DesktopAuraTopLevelWindowTest popup_window
;
396 ASSERT_NO_FATAL_FAILURE(popup_window
.CreateTopLevelWindow(
397 gfx::Rect(0, 0, 200, 200), false));
399 RunPendingMessages();
400 ASSERT_NO_FATAL_FAILURE(popup_window
.DestroyOwnedWindow());
401 RunPendingMessages();
404 // This test validates that when a top level owned popup Aura window is
405 // resized, the widget is resized as well.
406 TEST_F(DesktopAuraWidgetTest
, TopLevelOwnedPopupResizeTest
) {
407 DesktopAuraTopLevelWindowTest popup_window
;
409 popup_window
.set_use_async_mode(false);
411 ASSERT_NO_FATAL_FAILURE(popup_window
.CreateTopLevelWindow(
412 gfx::Rect(0, 0, 200, 200), false));
414 gfx::Rect
new_size(0, 0, 400, 400);
415 popup_window
.owned_window()->SetBounds(new_size
);
417 EXPECT_EQ(popup_window
.top_level_widget()->GetNativeView()->bounds().size(),
420 ASSERT_NO_FATAL_FAILURE(popup_window
.DestroyOwnedWindow());
423 // This test validates that when a top level owned popup Aura window is
424 // repositioned, the widget is repositioned as well.
425 TEST_F(DesktopAuraWidgetTest
, TopLevelOwnedPopupRepositionTest
) {
426 DesktopAuraTopLevelWindowTest popup_window
;
428 popup_window
.set_use_async_mode(false);
430 ASSERT_NO_FATAL_FAILURE(popup_window
.CreateTopLevelWindow(
431 gfx::Rect(0, 0, 200, 200), false));
433 gfx::Rect
new_pos(10, 10, 400, 400);
434 popup_window
.owned_window()->SetBoundsInScreen(
436 gfx::Screen::GetScreenFor(
437 popup_window
.owned_window())->GetDisplayNearestPoint(gfx::Point()));
440 popup_window
.top_level_widget()->GetWindowBoundsInScreen());
442 ASSERT_NO_FATAL_FAILURE(popup_window
.DestroyOwnedWindow());
445 // The following code verifies we can correctly destroy a Widget from a mouse
446 // enter/exit. We could test move/drag/enter/exit but in general we don't run
447 // nested message loops from such events, nor has the code ever really dealt
448 // with this situation.
450 // Generates two moves (first generates enter, second real move), a press, drag
451 // and release stopping at |last_event_type|.
452 void GenerateMouseEvents(Widget
* widget
, ui::EventType last_event_type
) {
453 const gfx::Rect
screen_bounds(widget
->GetWindowBoundsInScreen());
454 ui::MouseEvent
move_event(ui::ET_MOUSE_MOVED
, screen_bounds
.CenterPoint(),
455 screen_bounds
.CenterPoint(), ui::EventTimeForNow(),
457 ui::EventProcessor
* dispatcher
= WidgetTest::GetEventProcessor(widget
);
458 ui::EventDispatchDetails details
= dispatcher
->OnEventFromSource(&move_event
);
459 if (last_event_type
== ui::ET_MOUSE_ENTERED
|| details
.dispatcher_destroyed
)
461 details
= dispatcher
->OnEventFromSource(&move_event
);
462 if (last_event_type
== ui::ET_MOUSE_MOVED
|| details
.dispatcher_destroyed
)
465 ui::MouseEvent
press_event(ui::ET_MOUSE_PRESSED
, screen_bounds
.CenterPoint(),
466 screen_bounds
.CenterPoint(), ui::EventTimeForNow(),
468 details
= dispatcher
->OnEventFromSource(&press_event
);
469 if (last_event_type
== ui::ET_MOUSE_PRESSED
|| details
.dispatcher_destroyed
)
472 gfx::Point
end_point(screen_bounds
.CenterPoint());
473 end_point
.Offset(1, 1);
474 ui::MouseEvent
drag_event(ui::ET_MOUSE_DRAGGED
, end_point
, end_point
,
475 ui::EventTimeForNow(), 0, 0);
476 details
= dispatcher
->OnEventFromSource(&drag_event
);
477 if (last_event_type
== ui::ET_MOUSE_DRAGGED
|| details
.dispatcher_destroyed
)
480 ui::MouseEvent
release_event(ui::ET_MOUSE_RELEASED
, end_point
, end_point
,
481 ui::EventTimeForNow(), 0, 0);
482 details
= dispatcher
->OnEventFromSource(&release_event
);
483 if (details
.dispatcher_destroyed
)
487 // Creates a widget and invokes GenerateMouseEvents() with |last_event_type|.
488 void RunCloseWidgetDuringDispatchTest(WidgetTest
* test
,
489 ui::EventType last_event_type
) {
490 // |widget| is deleted by CloseWidgetView.
491 Widget
* widget
= new Widget
;
492 Widget::InitParams params
=
493 test
->CreateParams(Widget::InitParams::TYPE_POPUP
);
494 params
.native_widget
= new PlatformDesktopNativeWidget(widget
);
495 params
.bounds
= gfx::Rect(0, 0, 50, 100);
496 widget
->Init(params
);
497 widget
->SetContentsView(new CloseWidgetView(last_event_type
));
499 GenerateMouseEvents(widget
, last_event_type
);
502 // Verifies deleting the widget from a mouse pressed event doesn't crash.
503 TEST_F(DesktopAuraWidgetTest
, CloseWidgetDuringMousePress
) {
504 RunCloseWidgetDuringDispatchTest(this, ui::ET_MOUSE_PRESSED
);
507 // Verifies deleting the widget from a mouse released event doesn't crash.
508 TEST_F(DesktopAuraWidgetTest
, CloseWidgetDuringMouseReleased
) {
509 RunCloseWidgetDuringDispatchTest(this, ui::ET_MOUSE_RELEASED
);
512 // Provides functionality to create a window modal dialog.
513 class ModalDialogDelegate
: public DialogDelegateView
{
515 ModalDialogDelegate() {}
516 ~ModalDialogDelegate() override
{}
518 // WidgetDelegate overrides.
519 ui::ModalType
GetModalType() const override
{ return ui::MODAL_TYPE_WINDOW
; }
522 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate
);
525 // This test verifies that whether mouse events when a modal dialog is
526 // displayed are eaten or recieved by the dialog.
527 TEST_F(WidgetTest
, WindowMouseModalityTest
) {
528 // Create a top level widget.
529 Widget top_level_widget
;
530 Widget::InitParams init_params
=
531 CreateParams(Widget::InitParams::TYPE_WINDOW
);
532 init_params
.show_state
= ui::SHOW_STATE_NORMAL
;
533 gfx::Rect
initial_bounds(0, 0, 500, 500);
534 init_params
.bounds
= initial_bounds
;
535 init_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
536 init_params
.native_widget
=
537 new PlatformDesktopNativeWidget(&top_level_widget
);
538 top_level_widget
.Init(init_params
);
539 top_level_widget
.Show();
540 EXPECT_TRUE(top_level_widget
.IsVisible());
542 // Create a view and validate that a mouse moves makes it to the view.
543 EventCountView
* widget_view
= new EventCountView();
544 widget_view
->SetBounds(0, 0, 10, 10);
545 top_level_widget
.GetRootView()->AddChildView(widget_view
);
547 gfx::Point
cursor_location_main(5, 5);
548 ui::MouseEvent
move_main(ui::ET_MOUSE_MOVED
, cursor_location_main
,
549 cursor_location_main
, ui::EventTimeForNow(),
550 ui::EF_NONE
, ui::EF_NONE
);
551 ui::EventDispatchDetails details
=
552 GetEventProcessor(&top_level_widget
)->OnEventFromSource(&move_main
);
553 ASSERT_FALSE(details
.dispatcher_destroyed
);
555 EXPECT_EQ(1, widget_view
->GetEventCount(ui::ET_MOUSE_ENTERED
));
556 widget_view
->ResetCounts();
558 // Create a modal dialog and validate that a mouse down message makes it to
559 // the main view within the dialog.
561 // This instance will be destroyed when the dialog is destroyed.
562 ModalDialogDelegate
* dialog_delegate
= new ModalDialogDelegate
;
564 Widget
* modal_dialog_widget
= views::DialogDelegate::CreateDialogWidget(
565 dialog_delegate
, NULL
, top_level_widget
.GetNativeView());
566 modal_dialog_widget
->SetBounds(gfx::Rect(100, 100, 200, 200));
567 EventCountView
* dialog_widget_view
= new EventCountView();
568 dialog_widget_view
->SetBounds(0, 0, 50, 50);
569 modal_dialog_widget
->GetRootView()->AddChildView(dialog_widget_view
);
570 modal_dialog_widget
->Show();
571 EXPECT_TRUE(modal_dialog_widget
->IsVisible());
573 gfx::Point
cursor_location_dialog(100, 100);
574 ui::MouseEvent
mouse_down_dialog(
575 ui::ET_MOUSE_PRESSED
, cursor_location_dialog
, cursor_location_dialog
,
576 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
577 details
= GetEventProcessor(&top_level_widget
)->OnEventFromSource(
579 ASSERT_FALSE(details
.dispatcher_destroyed
);
580 EXPECT_EQ(1, dialog_widget_view
->GetEventCount(ui::ET_MOUSE_PRESSED
));
582 // Send a mouse move message to the main window. It should not be received by
583 // the main window as the modal dialog is still active.
584 gfx::Point
cursor_location_main2(6, 6);
585 ui::MouseEvent
mouse_down_main(ui::ET_MOUSE_MOVED
, cursor_location_main2
,
586 cursor_location_main2
, ui::EventTimeForNow(),
587 ui::EF_NONE
, ui::EF_NONE
);
588 details
= GetEventProcessor(&top_level_widget
)->OnEventFromSource(
590 ASSERT_FALSE(details
.dispatcher_destroyed
);
591 EXPECT_EQ(0, widget_view
->GetEventCount(ui::ET_MOUSE_MOVED
));
593 modal_dialog_widget
->CloseNow();
594 top_level_widget
.CloseNow();
598 // Tests whether we can activate the top level widget when a modal dialog is
600 TEST_F(WidgetTest
, WindowModalityActivationTest
) {
601 TestDesktopWidgetDelegate widget_delegate
;
602 widget_delegate
.InitWidget(CreateParams(Widget::InitParams::TYPE_WINDOW
));
604 Widget
* top_level_widget
= widget_delegate
.GetWidget();
605 top_level_widget
->Show();
606 EXPECT_TRUE(top_level_widget
->IsVisible());
608 HWND win32_window
= views::HWNDForWidget(top_level_widget
);
609 EXPECT_TRUE(::IsWindow(win32_window
));
611 // This instance will be destroyed when the dialog is destroyed.
612 ModalDialogDelegate
* dialog_delegate
= new ModalDialogDelegate
;
614 // We should be able to activate the window even if the WidgetDelegate
615 // says no, when a modal dialog is active.
616 widget_delegate
.set_can_activate(false);
618 Widget
* modal_dialog_widget
= views::DialogDelegate::CreateDialogWidget(
619 dialog_delegate
, NULL
, top_level_widget
->GetNativeView());
620 modal_dialog_widget
->SetBounds(gfx::Rect(100, 100, 200, 200));
621 modal_dialog_widget
->Show();
622 EXPECT_TRUE(modal_dialog_widget
->IsVisible());
624 LRESULT activate_result
= ::SendMessage(
627 reinterpret_cast<WPARAM
>(win32_window
),
628 MAKELPARAM(WM_LBUTTONDOWN
, HTCLIENT
));
629 EXPECT_EQ(activate_result
, MA_ACTIVATE
);
631 modal_dialog_widget
->CloseNow();
633 #endif // defined(OS_WIN)