1 // Copyright (c) 2012 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/native_widget_aura.h"
7 #include "base/basictypes.h"
8 #include "base/command_line.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12 #include "ui/aura/client/aura_constants.h"
13 #include "ui/aura/env.h"
14 #include "ui/aura/layout_manager.h"
15 #include "ui/aura/test/aura_test_base.h"
16 #include "ui/aura/window.h"
17 #include "ui/aura/window_tree_host.h"
18 #include "ui/events/event.h"
19 #include "ui/events/event_utils.h"
20 #include "ui/gfx/screen.h"
21 #include "ui/views/layout/fill_layout.h"
22 #include "ui/views/widget/root_view.h"
23 #include "ui/views/widget/widget_delegate.h"
24 #include "ui/wm/core/default_activation_client.h"
29 NativeWidgetAura
* Init(aura::Window
* parent
, Widget
* widget
) {
30 Widget::InitParams
params(Widget::InitParams::TYPE_POPUP
);
31 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
32 params
.parent
= parent
;
34 return static_cast<NativeWidgetAura
*>(widget
->native_widget());
37 class NativeWidgetAuraTest
: public aura::test::AuraTestBase
{
39 NativeWidgetAuraTest() {}
40 virtual ~NativeWidgetAuraTest() {}
42 // testing::Test overrides:
43 virtual void SetUp() OVERRIDE
{
44 AuraTestBase::SetUp();
45 new wm::DefaultActivationClient(root_window());
46 host()->SetBounds(gfx::Rect(640, 480));
50 DISALLOW_COPY_AND_ASSIGN(NativeWidgetAuraTest
);
53 TEST_F(NativeWidgetAuraTest
, CenterWindowLargeParent
) {
54 // Make a parent window larger than the host represented by
55 // WindowEventDispatcher.
56 scoped_ptr
<aura::Window
> parent(new aura::Window(NULL
));
57 parent
->Init(aura::WINDOW_LAYER_NOT_DRAWN
);
58 parent
->SetBounds(gfx::Rect(0, 0, 1024, 800));
59 scoped_ptr
<Widget
> widget(new Widget());
60 NativeWidgetAura
* window
= Init(parent
.get(), widget
.get());
62 window
->CenterWindow(gfx::Size(100, 100));
63 EXPECT_EQ(gfx::Rect( (640 - 100) / 2,
66 window
->GetNativeWindow()->bounds());
70 TEST_F(NativeWidgetAuraTest
, CenterWindowSmallParent
) {
71 // Make a parent window smaller than the host represented by
72 // WindowEventDispatcher.
73 scoped_ptr
<aura::Window
> parent(new aura::Window(NULL
));
74 parent
->Init(aura::WINDOW_LAYER_NOT_DRAWN
);
75 parent
->SetBounds(gfx::Rect(0, 0, 480, 320));
76 scoped_ptr
<Widget
> widget(new Widget());
77 NativeWidgetAura
* window
= Init(parent
.get(), widget
.get());
79 window
->CenterWindow(gfx::Size(100, 100));
80 EXPECT_EQ(gfx::Rect( (480 - 100) / 2,
83 window
->GetNativeWindow()->bounds());
87 // Verifies CenterWindow() constrains to parent size.
88 TEST_F(NativeWidgetAuraTest
, CenterWindowSmallParentNotAtOrigin
) {
89 // Make a parent window smaller than the host represented by
90 // WindowEventDispatcher and offset it slightly from the origin.
91 scoped_ptr
<aura::Window
> parent(new aura::Window(NULL
));
92 parent
->Init(aura::WINDOW_LAYER_NOT_DRAWN
);
93 parent
->SetBounds(gfx::Rect(20, 40, 480, 320));
94 scoped_ptr
<Widget
> widget(new Widget());
95 NativeWidgetAura
* window
= Init(parent
.get(), widget
.get());
96 window
->CenterWindow(gfx::Size(500, 600));
98 // |window| should be no bigger than |parent|.
99 EXPECT_EQ("20,40 480x320", window
->GetNativeWindow()->bounds().ToString());
103 class TestLayoutManagerBase
: public aura::LayoutManager
{
105 TestLayoutManagerBase() {}
106 virtual ~TestLayoutManagerBase() {}
108 // aura::LayoutManager:
109 virtual void OnWindowResized() OVERRIDE
{}
110 virtual void OnWindowAddedToLayout(aura::Window
* child
) OVERRIDE
{}
111 virtual void OnWillRemoveWindowFromLayout(aura::Window
* child
) OVERRIDE
{}
112 virtual void OnWindowRemovedFromLayout(aura::Window
* child
) OVERRIDE
{}
113 virtual void OnChildWindowVisibilityChanged(aura::Window
* child
,
114 bool visible
) OVERRIDE
{}
115 virtual void SetChildBounds(aura::Window
* child
,
116 const gfx::Rect
& requested_bounds
) OVERRIDE
{}
119 DISALLOW_COPY_AND_ASSIGN(TestLayoutManagerBase
);
122 // Used by ShowMaximizedDoesntBounceAround. See it for details.
123 class MaximizeLayoutManager
: public TestLayoutManagerBase
{
125 MaximizeLayoutManager() {}
126 virtual ~MaximizeLayoutManager() {}
129 // aura::LayoutManager:
130 virtual void OnWindowAddedToLayout(aura::Window
* child
) OVERRIDE
{
131 // This simulates what happens when adding a maximized window.
132 SetChildBoundsDirect(child
, gfx::Rect(0, 0, 300, 300));
135 DISALLOW_COPY_AND_ASSIGN(MaximizeLayoutManager
);
138 // This simulates BrowserView, which creates a custom RootView so that
139 // OnNativeWidgetSizeChanged that is invoked during Init matters.
140 class TestWidget
: public views::Widget
{
142 TestWidget() : did_size_change_more_than_once_(false) {
145 // Returns true if the size changes to a non-empty size, and then to another
147 bool did_size_change_more_than_once() const {
148 return did_size_change_more_than_once_
;
151 virtual void OnNativeWidgetSizeChanged(const gfx::Size
& new_size
) OVERRIDE
{
152 if (last_size_
.IsEmpty())
153 last_size_
= new_size
;
154 else if (!did_size_change_more_than_once_
&& new_size
!= last_size_
)
155 did_size_change_more_than_once_
= true;
156 Widget::OnNativeWidgetSizeChanged(new_size
);
160 bool did_size_change_more_than_once_
;
161 gfx::Size last_size_
;
163 DISALLOW_COPY_AND_ASSIGN(TestWidget
);
166 // Verifies the size of the widget doesn't change more than once during Init if
167 // the window ends up maximized. This is important as otherwise
168 // RenderWidgetHostViewAura ends up getting resized during construction, which
169 // leads to noticable flashes.
170 TEST_F(NativeWidgetAuraTest
, ShowMaximizedDoesntBounceAround
) {
171 root_window()->SetBounds(gfx::Rect(0, 0, 640, 480));
172 root_window()->SetLayoutManager(new MaximizeLayoutManager
);
173 scoped_ptr
<TestWidget
> widget(new TestWidget());
174 Widget::InitParams
params(Widget::InitParams::TYPE_WINDOW
);
175 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
176 params
.parent
= NULL
;
177 params
.context
= root_window();
178 params
.show_state
= ui::SHOW_STATE_MAXIMIZED
;
179 params
.bounds
= gfx::Rect(10, 10, 100, 200);
180 widget
->Init(params
);
181 EXPECT_FALSE(widget
->did_size_change_more_than_once());
185 class PropertyTestLayoutManager
: public TestLayoutManagerBase
{
187 PropertyTestLayoutManager() : added_(false) {}
188 virtual ~PropertyTestLayoutManager() {}
190 bool added() const { return added_
; }
193 // aura::LayoutManager:
194 virtual void OnWindowAddedToLayout(aura::Window
* child
) OVERRIDE
{
195 EXPECT_TRUE(child
->GetProperty(aura::client::kCanMaximizeKey
));
196 EXPECT_TRUE(child
->GetProperty(aura::client::kCanResizeKey
));
202 DISALLOW_COPY_AND_ASSIGN(PropertyTestLayoutManager
);
205 class PropertyTestWidgetDelegate
: public views::WidgetDelegate
{
207 explicit PropertyTestWidgetDelegate(Widget
* widget
) : widget_(widget
) {}
208 virtual ~PropertyTestWidgetDelegate() {}
211 // views::WidgetDelegate:
212 virtual bool CanMaximize() const OVERRIDE
{
215 virtual bool CanResize() const OVERRIDE
{
218 virtual void DeleteDelegate() OVERRIDE
{
221 virtual Widget
* GetWidget() OVERRIDE
{
224 virtual const Widget
* GetWidget() const OVERRIDE
{
229 DISALLOW_COPY_AND_ASSIGN(PropertyTestWidgetDelegate
);
232 // Verifies that the kCanMaximizeKey/kCanReizeKey have the correct
233 // value when added to the layout manager.
234 TEST_F(NativeWidgetAuraTest
, TestPropertiesWhenAddedToLayout
) {
235 root_window()->SetBounds(gfx::Rect(0, 0, 640, 480));
236 PropertyTestLayoutManager
* layout_manager
= new PropertyTestLayoutManager();
237 root_window()->SetLayoutManager(layout_manager
);
238 scoped_ptr
<TestWidget
> widget(new TestWidget());
239 Widget::InitParams
params(Widget::InitParams::TYPE_WINDOW
);
240 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
241 params
.delegate
= new PropertyTestWidgetDelegate(widget
.get());
242 params
.parent
= NULL
;
243 params
.context
= root_window();
244 widget
->Init(params
);
245 EXPECT_TRUE(layout_manager
->added());
249 TEST_F(NativeWidgetAuraTest
, GetClientAreaScreenBounds
) {
251 Widget::InitParams
params(Widget::InitParams::TYPE_WINDOW
);
252 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
253 params
.context
= root_window();
254 params
.bounds
.SetRect(10, 20, 300, 400);
255 scoped_ptr
<Widget
> widget(new Widget());
256 widget
->Init(params
);
258 // For Aura, client area bounds match window bounds.
259 gfx::Rect client_bounds
= widget
->GetClientAreaBoundsInScreen();
260 EXPECT_EQ(10, client_bounds
.x());
261 EXPECT_EQ(20, client_bounds
.y());
262 EXPECT_EQ(300, client_bounds
.width());
263 EXPECT_EQ(400, client_bounds
.height());
266 // View subclass that tracks whether it has gotten a gesture event.
267 class GestureTrackingView
: public views::View
{
269 GestureTrackingView()
270 : got_gesture_event_(false),
271 consume_gesture_event_(true) {}
273 void set_consume_gesture_event(bool value
) {
274 consume_gesture_event_
= value
;
277 void clear_got_gesture_event() {
278 got_gesture_event_
= false;
280 bool got_gesture_event() const {
281 return got_gesture_event_
;
285 virtual void OnGestureEvent(ui::GestureEvent
* event
) OVERRIDE
{
286 got_gesture_event_
= true;
287 if (consume_gesture_event_
)
288 event
->StopPropagation();
292 // Was OnGestureEvent() invoked?
293 bool got_gesture_event_
;
295 // Dictates what OnGestureEvent() returns.
296 bool consume_gesture_event_
;
298 DISALLOW_COPY_AND_ASSIGN(GestureTrackingView
);
301 // Verifies a capture isn't set on touch press and that the view that gets
302 // the press gets the release.
303 TEST_F(NativeWidgetAuraTest
, DontCaptureOnGesture
) {
304 // Create two views (both sized the same). |child| is configured not to
305 // consume the gesture event.
306 GestureTrackingView
* view
= new GestureTrackingView();
307 GestureTrackingView
* child
= new GestureTrackingView();
308 child
->set_consume_gesture_event(false);
309 view
->SetLayoutManager(new FillLayout
);
310 view
->AddChildView(child
);
311 scoped_ptr
<TestWidget
> widget(new TestWidget());
312 Widget::InitParams
params(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
313 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
314 params
.context
= root_window();
315 params
.bounds
= gfx::Rect(0, 0, 100, 200);
316 widget
->Init(params
);
317 widget
->SetContentsView(view
);
320 ui::TouchEvent
press(
321 ui::ET_TOUCH_PRESSED
, gfx::Point(41, 51), 1, ui::EventTimeForNow());
322 ui::EventDispatchDetails details
=
323 event_processor()->OnEventFromSource(&press
);
324 ASSERT_FALSE(details
.dispatcher_destroyed
);
325 // Both views should get the press.
326 EXPECT_TRUE(view
->got_gesture_event());
327 EXPECT_TRUE(child
->got_gesture_event());
328 view
->clear_got_gesture_event();
329 child
->clear_got_gesture_event();
330 // Touch events should not automatically grab capture.
331 EXPECT_FALSE(widget
->HasCapture());
333 // Release touch. Only |view| should get the release since that it consumed
335 ui::TouchEvent
release(
336 ui::ET_TOUCH_RELEASED
, gfx::Point(250, 251), 1, ui::EventTimeForNow());
337 details
= event_processor()->OnEventFromSource(&release
);
338 ASSERT_FALSE(details
.dispatcher_destroyed
);
339 EXPECT_TRUE(view
->got_gesture_event());
340 EXPECT_FALSE(child
->got_gesture_event());
341 view
->clear_got_gesture_event();
343 // Work around for bug in NativeWidgetAura.
344 // TODO: fix bug and remove this.
348 // Verifies views with layers are targeted for events properly.
349 TEST_F(NativeWidgetAuraTest
, PreferViewLayersToChildWindows
) {
350 // Create two widgets: |parent| and |child|. |child| is a child of |parent|.
351 views::View
* parent_root
= new views::View
;
352 scoped_ptr
<Widget
> parent(new Widget());
353 Widget::InitParams
parent_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
354 parent_params
.ownership
=
355 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
356 parent_params
.context
= root_window();
357 parent
->Init(parent_params
);
358 parent
->SetContentsView(parent_root
);
359 parent
->SetBounds(gfx::Rect(0, 0, 400, 400));
362 scoped_ptr
<Widget
> child(new Widget());
363 Widget::InitParams
child_params(Widget::InitParams::TYPE_CONTROL
);
364 child_params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
365 child_params
.parent
= parent
->GetNativeWindow();
366 child
->Init(child_params
);
367 child
->SetBounds(gfx::Rect(0, 0, 200, 200));
370 // Point is over |child|.
371 EXPECT_EQ(child
->GetNativeWindow(),
372 parent
->GetNativeWindow()->GetEventHandlerForPoint(
373 gfx::Point(50, 50)));
375 // Create a view with a layer and stack it at the bottom (below |child|).
376 views::View
* view_with_layer
= new views::View
;
377 parent_root
->AddChildView(view_with_layer
);
378 view_with_layer
->SetBounds(0, 0, 50, 50);
379 view_with_layer
->SetPaintToLayer(true);
381 // Make sure that |child| still gets the event.
382 EXPECT_EQ(child
->GetNativeWindow(),
383 parent
->GetNativeWindow()->GetEventHandlerForPoint(
384 gfx::Point(20, 20)));
386 // Move |view_with_layer| to the top and make sure it gets the
387 // event when the point is within |view_with_layer|'s bounds.
388 view_with_layer
->layer()->parent()->StackAtTop(
389 view_with_layer
->layer());
390 EXPECT_EQ(parent
->GetNativeWindow(),
391 parent
->GetNativeWindow()->GetEventHandlerForPoint(
392 gfx::Point(20, 20)));
394 // Point is over |child|, it should get the event.
395 EXPECT_EQ(child
->GetNativeWindow(),
396 parent
->GetNativeWindow()->GetEventHandlerForPoint(
397 gfx::Point(70, 70)));
399 delete view_with_layer
;
400 view_with_layer
= NULL
;
402 EXPECT_EQ(child
->GetNativeWindow(),
403 parent
->GetNativeWindow()->GetEventHandlerForPoint(
404 gfx::Point(20, 20)));
406 // Work around for bug in NativeWidgetAura.
407 // TODO: fix bug and remove this.
411 // Verifies that widget->FlashFrame() sets aura::client::kDrawAttentionKey,
412 // and activating the window clears it.
413 TEST_F(NativeWidgetAuraTest
, FlashFrame
) {
414 scoped_ptr
<Widget
> widget(new Widget());
415 Widget::InitParams
params(Widget::InitParams::TYPE_WINDOW
);
416 params
.context
= root_window();
417 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
418 widget
->Init(params
);
419 aura::Window
* window
= widget
->GetNativeWindow();
420 EXPECT_FALSE(window
->GetProperty(aura::client::kDrawAttentionKey
));
421 widget
->FlashFrame(true);
422 EXPECT_TRUE(window
->GetProperty(aura::client::kDrawAttentionKey
));
423 widget
->FlashFrame(false);
424 EXPECT_FALSE(window
->GetProperty(aura::client::kDrawAttentionKey
));
425 widget
->FlashFrame(true);
426 EXPECT_TRUE(window
->GetProperty(aura::client::kDrawAttentionKey
));
428 EXPECT_FALSE(window
->GetProperty(aura::client::kDrawAttentionKey
));
431 TEST_F(NativeWidgetAuraTest
, NoCrashOnThemeAfterClose
) {
432 scoped_ptr
<aura::Window
> parent(new aura::Window(NULL
));
433 parent
->Init(aura::WINDOW_LAYER_NOT_DRAWN
);
434 parent
->SetBounds(gfx::Rect(0, 0, 480, 320));
435 scoped_ptr
<Widget
> widget(new Widget());
436 Init(parent
.get(), widget
.get());
439 base::MessageLoop::current()->RunUntilIdle();
440 widget
->GetNativeTheme(); // Shouldn't crash.
443 // Used to track calls to WidgetDelegate::OnWidgetMove().
444 class MoveTestWidgetDelegate
: public WidgetDelegateView
{
446 MoveTestWidgetDelegate() : got_move_(false) {}
447 virtual ~MoveTestWidgetDelegate() {}
449 void ClearGotMove() { got_move_
= false; }
450 bool got_move() const { return got_move_
; }
452 // WidgetDelegate overrides:
453 virtual void OnWidgetMove() OVERRIDE
{ got_move_
= true; }
458 DISALLOW_COPY_AND_ASSIGN(MoveTestWidgetDelegate
);
461 // This test simulates what happens when a window is normally maximized. That
462 // is, it's layer is acquired for animation then the window is maximized.
463 // Acquiring the layer resets the bounds of the window. This test verifies the
464 // Widget is still notified correctly of a move in this case.
465 TEST_F(NativeWidgetAuraTest
, OnWidgetMovedInvokedAfterAcquireLayer
) {
466 // |delegate| deletes itself when the widget is destroyed.
467 MoveTestWidgetDelegate
* delegate
= new MoveTestWidgetDelegate
;
469 Widget::CreateWindowWithContextAndBounds(delegate
,
471 gfx::Rect(10, 10, 100, 200));
473 delegate
->ClearGotMove();
474 // Simulate a maximize with animation.
475 delete widget
->GetNativeView()->RecreateLayer().release();
476 widget
->SetBounds(gfx::Rect(0, 0, 500, 500));
477 EXPECT_TRUE(delegate
->got_move());