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 "ash/wm/header_painter.h"
7 #include "ash/ash_constants.h"
8 #include "ash/root_window_settings.h"
10 #include "ash/shell_window_ids.h"
11 #include "ash/test/ash_test_base.h"
12 #include "ash/wm/caption_buttons/frame_caption_button_container_view.h"
13 #include "ash/wm/window_state.h"
14 #include "ash/wm/window_util.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/message_loop/message_loop.h"
17 #include "grit/ash_resources.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "ui/aura/client/aura_constants.h"
20 #include "ui/aura/client/window_tree_client.h"
21 #include "ui/aura/root_window.h"
22 #include "ui/aura/window_observer.h"
23 #include "ui/base/hit_test.h"
24 #include "ui/gfx/font.h"
25 #include "ui/gfx/screen.h"
26 #include "ui/views/widget/widget.h"
27 #include "ui/views/widget/widget_delegate.h"
28 #include "ui/views/widget/widget_observer.h"
29 #include "ui/views/window/non_client_view.h"
31 using ash::HeaderPainter
;
32 using views::NonClientFrameView
;
37 class ResizableWidgetDelegate
: public views::WidgetDelegate
{
39 ResizableWidgetDelegate(views::Widget
* widget
) {
43 virtual bool CanResize() const OVERRIDE
{ return true; }
44 // Implementations of the widget class.
45 virtual views::Widget
* GetWidget() OVERRIDE
{ return widget_
; }
46 virtual const views::Widget
* GetWidget() const OVERRIDE
{ return widget_
; }
47 virtual void DeleteDelegate() OVERRIDE
{
52 views::Widget
* widget_
;
54 DISALLOW_COPY_AND_ASSIGN(ResizableWidgetDelegate
);
57 class WindowRepaintChecker
: public aura::WindowObserver
{
59 explicit WindowRepaintChecker(aura::Window
* window
)
60 : is_paint_scheduled_(false) {
61 window
->AddObserver(this);
63 virtual ~WindowRepaintChecker() {
66 bool IsPaintScheduledAndReset() {
67 bool result
= is_paint_scheduled_
;
68 is_paint_scheduled_
= false;
73 // aura::WindowObserver overrides:
74 virtual void OnWindowPaintScheduled(aura::Window
* window
,
75 const gfx::Rect
& region
) OVERRIDE
{
76 is_paint_scheduled_
= true;
78 virtual void OnWindowDestroying(aura::Window
* window
) OVERRIDE
{
79 window
->RemoveObserver(this);
82 bool is_paint_scheduled_
;
84 DISALLOW_COPY_AND_ASSIGN(WindowRepaintChecker
);
87 // Modifies the values of kInactiveWindowOpacity, kActiveWindowOpacity, and
88 // kSoloWindowOpacity for the lifetime of the class. This is useful so that
89 // the constants each have different values.
90 class ScopedOpacityConstantModifier
{
92 ScopedOpacityConstantModifier()
93 : initial_active_window_opacity_(
94 ash::HeaderPainter::kActiveWindowOpacity
),
95 initial_inactive_window_opacity_(
96 ash::HeaderPainter::kInactiveWindowOpacity
),
97 initial_solo_window_opacity_(ash::HeaderPainter::kSoloWindowOpacity
) {
98 ash::HeaderPainter::kActiveWindowOpacity
= 100;
99 ash::HeaderPainter::kInactiveWindowOpacity
= 120;
100 ash::HeaderPainter::kSoloWindowOpacity
= 140;
102 ~ScopedOpacityConstantModifier() {
103 ash::HeaderPainter::kActiveWindowOpacity
= initial_active_window_opacity_
;
104 ash::HeaderPainter::kInactiveWindowOpacity
=
105 initial_inactive_window_opacity_
;
106 ash::HeaderPainter::kSoloWindowOpacity
= initial_solo_window_opacity_
;
110 int initial_active_window_opacity_
;
111 int initial_inactive_window_opacity_
;
112 int initial_solo_window_opacity_
;
114 DISALLOW_COPY_AND_ASSIGN(ScopedOpacityConstantModifier
);
117 // Creates a new HeaderPainter with empty buttons. Caller owns the memory.
118 HeaderPainter
* CreateTestPainter(Widget
* widget
) {
119 HeaderPainter
* painter
= new HeaderPainter();
120 NonClientFrameView
* frame_view
= widget
->non_client_view()->frame_view();
121 ash::FrameCaptionButtonContainerView
* container
=
122 new ash::FrameCaptionButtonContainerView(
124 ash::FrameCaptionButtonContainerView::MINIMIZE_ALLOWED
);
125 // Add the container to the widget's non-client frame view so that it will be
126 // deleted when the widget is destroyed.
127 frame_view
->AddChildView(container
);
128 painter
->Init(widget
, frame_view
, NULL
, container
);
132 // Self-owned manager of the frame painter which deletes the painter and itself
133 // when its widget is closed.
134 class HeaderPainterOwner
: views::WidgetObserver
{
136 explicit HeaderPainterOwner(Widget
* widget
)
137 : header_painter_(CreateTestPainter(widget
)) {
138 widget
->AddObserver(this);
141 virtual ~HeaderPainterOwner() {}
143 HeaderPainter
* header_painter() { return header_painter_
.get(); }
146 virtual void OnWidgetDestroying(Widget
* widget
) OVERRIDE
{
147 widget
->RemoveObserver(this);
148 // Do not delete directly here, since the task of HeaderPainter causing
149 // the crash of crbug.com/273310 may run after this class handles this
151 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
154 scoped_ptr
<HeaderPainter
> header_painter_
;
156 DISALLOW_COPY_AND_ASSIGN(HeaderPainterOwner
);
163 class HeaderPainterTest
: public ash::test::AshTestBase
{
165 // Creates a test widget that owns its native widget.
166 Widget
* CreateTestWidget() {
167 Widget
* widget
= new Widget
;
168 Widget::InitParams params
;
169 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
170 params
.context
= CurrentContext();
171 widget
->Init(params
);
175 Widget
* CreateAlwaysOnTopWidget() {
176 Widget
* widget
= new Widget
;
177 Widget::InitParams params
;
178 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
179 params
.context
= CurrentContext();
180 params
.keep_on_top
= true;
181 widget
->Init(params
);
185 Widget
* CreatePanelWidget() {
186 Widget
* widget
= new Widget
;
187 Widget::InitParams params
;
188 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
189 params
.context
= CurrentContext();
190 params
.type
= Widget::InitParams::TYPE_PANEL
;
191 widget
->Init(params
);
195 Widget
* CreateResizableWidget() {
196 Widget
* widget
= new Widget
;
197 Widget::InitParams params
;
198 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
199 params
.context
= CurrentContext();
200 params
.keep_on_top
= true;
201 params
.delegate
= new ResizableWidgetDelegate(widget
);
202 params
.type
= Widget::InitParams::TYPE_WINDOW
;
203 widget
->Init(params
);
208 TEST_F(HeaderPainterTest
, CreateAndDeleteSingleWindow
) {
209 // Ensure that creating/deleting a window works well and doesn't cause
210 // crashes. See crbug.com/155634
211 aura::Window
* root
= Shell::GetTargetRootWindow();
213 scoped_ptr
<Widget
> widget(CreateTestWidget());
214 scoped_ptr
<HeaderPainter
> painter(CreateTestPainter(widget
.get()));
217 // We only have one window, so it should use a solo header.
218 EXPECT_TRUE(painter
->UseSoloWindowHeader());
219 EXPECT_TRUE(internal::GetRootWindowSettings(root
)->solo_window_header
);
223 EXPECT_FALSE(internal::GetRootWindowSettings(root
)->solo_window_header
);
225 // Recreate another window again.
226 widget
.reset(CreateTestWidget());
227 painter
.reset(CreateTestPainter(widget
.get()));
229 EXPECT_TRUE(painter
->UseSoloWindowHeader());
230 EXPECT_TRUE(internal::GetRootWindowSettings(root
)->solo_window_header
);
233 TEST_F(HeaderPainterTest
, UseSoloWindowHeader
) {
234 // Create a widget and a painter for it.
235 scoped_ptr
<Widget
> w1(CreateTestWidget());
236 scoped_ptr
<HeaderPainter
> p1(CreateTestPainter(w1
.get()));
239 // We only have one window, so it should use a solo header.
240 EXPECT_TRUE(p1
->UseSoloWindowHeader());
242 // Create a second widget and painter.
243 scoped_ptr
<Widget
> w2(CreateTestWidget());
244 scoped_ptr
<HeaderPainter
> p2(CreateTestPainter(w2
.get()));
247 // Now there are two windows, so we should not use solo headers. This only
248 // needs to test |p1| because "solo window headers" are a per-root-window
250 EXPECT_FALSE(p1
->UseSoloWindowHeader());
252 // Hide one window. Solo should be enabled.
254 EXPECT_TRUE(p1
->UseSoloWindowHeader());
256 // Show that window. Solo should be disabled.
258 EXPECT_FALSE(p1
->UseSoloWindowHeader());
260 // Minimize the second window. Solo should be enabled.
262 EXPECT_TRUE(p1
->UseSoloWindowHeader());
264 // Close the minimized window.
266 EXPECT_TRUE(p1
->UseSoloWindowHeader());
268 // Open an always-on-top widget (which lives in a different container).
269 scoped_ptr
<Widget
> w3(CreateAlwaysOnTopWidget());
270 scoped_ptr
<HeaderPainter
> p3(CreateTestPainter(w3
.get()));
272 EXPECT_FALSE(p3
->UseSoloWindowHeader());
274 // Close the always-on-top widget.
276 EXPECT_TRUE(p1
->UseSoloWindowHeader());
279 // An open V2 app window should cause browser windows not to use the
280 // solo window header.
281 TEST_F(HeaderPainterTest
, UseSoloWindowHeaderWithApp
) {
282 // Create a widget and a painter for it.
283 scoped_ptr
<Widget
> w1(CreateTestWidget());
284 scoped_ptr
<HeaderPainter
> p1(CreateTestPainter(w1
.get()));
287 // We only have one window, so it should use a solo header.
288 EXPECT_TRUE(p1
->UseSoloWindowHeader());
290 // Simulate a V2 app window, which is part of the active workspace but does
291 // not have a frame painter.
292 scoped_ptr
<Widget
> w2(CreateTestWidget());
295 // Now there are two windows, so we should not use solo headers.
296 EXPECT_FALSE(p1
->UseSoloWindowHeader());
298 // Minimize the app window. The first window should go solo again.
300 EXPECT_TRUE(p1
->UseSoloWindowHeader());
302 // Restoring the app window turns off solo headers.
304 EXPECT_FALSE(p1
->UseSoloWindowHeader());
307 // Panels should not "count" for computing solo window headers, and the panel
308 // itself should always have an opaque header.
309 TEST_F(HeaderPainterTest
, UseSoloWindowHeaderWithPanel
) {
310 // Create a widget and a painter for it.
311 scoped_ptr
<Widget
> w1(CreateTestWidget());
312 scoped_ptr
<HeaderPainter
> p1(CreateTestPainter(w1
.get()));
315 // We only have one window, so it should use a solo header.
316 EXPECT_TRUE(p1
->UseSoloWindowHeader());
318 // Create a panel and a painter for it.
319 scoped_ptr
<Widget
> w2(CreatePanelWidget());
320 scoped_ptr
<HeaderPainter
> p2(CreateTestPainter(w2
.get()));
323 // Despite two windows, the first window should still be considered "solo"
324 // because panels aren't included in the computation.
325 EXPECT_TRUE(p1
->UseSoloWindowHeader());
327 // The panel itself is not considered solo.
328 EXPECT_FALSE(p2
->UseSoloWindowHeader());
330 // Even after closing the first window, the panel is still not considered
333 EXPECT_FALSE(p2
->UseSoloWindowHeader());
336 // Modal dialogs should not use solo headers.
337 TEST_F(HeaderPainterTest
, UseSoloWindowHeaderModal
) {
338 // Create a widget and a painter for it.
339 scoped_ptr
<Widget
> w1(CreateTestWidget());
340 scoped_ptr
<HeaderPainter
> p1(CreateTestPainter(w1
.get()));
343 // We only have one window, so it should use a solo header.
344 EXPECT_TRUE(p1
->UseSoloWindowHeader());
346 // Create a fake modal window.
347 scoped_ptr
<Widget
> w2(CreateTestWidget());
348 scoped_ptr
<HeaderPainter
> p2(CreateTestPainter(w2
.get()));
349 w2
->GetNativeWindow()->SetProperty(aura::client::kModalKey
,
350 ui::MODAL_TYPE_WINDOW
);
353 // Despite two windows, the first window should still be considered "solo"
354 // because modal windows aren't included in the computation.
355 EXPECT_TRUE(p1
->UseSoloWindowHeader());
357 // The modal window itself is not considered solo.
358 EXPECT_FALSE(p2
->UseSoloWindowHeader());
361 // Constrained windows should not use solo headers.
362 TEST_F(HeaderPainterTest
, UseSoloWindowHeaderConstrained
) {
363 // Create a widget and a painter for it.
364 scoped_ptr
<Widget
> w1(CreateTestWidget());
365 scoped_ptr
<HeaderPainter
> p1(CreateTestPainter(w1
.get()));
368 // We only have one window, so it should use a solo header.
369 EXPECT_TRUE(p1
->UseSoloWindowHeader());
371 // Create a fake constrained window.
372 scoped_ptr
<Widget
> w2(CreateTestWidget());
373 scoped_ptr
<HeaderPainter
> p2(CreateTestPainter(w2
.get()));
374 w2
->GetNativeWindow()->SetProperty(ash::kConstrainedWindowKey
, true);
377 // Despite two windows, the first window should still be considered "solo"
378 // because constrained windows aren't included in the computation.
379 EXPECT_TRUE(p1
->UseSoloWindowHeader());
381 // The constrained window itself is not considered solo.
382 EXPECT_FALSE(p2
->UseSoloWindowHeader());
385 // Non-drawing windows should not affect the solo computation.
386 TEST_F(HeaderPainterTest
, UseSoloWindowHeaderNotDrawn
) {
387 // Create a widget and a painter for it.
388 scoped_ptr
<Widget
> widget(CreateTestWidget());
389 scoped_ptr
<HeaderPainter
> painter(CreateTestPainter(widget
.get()));
392 // We only have one window, so it should use a solo header.
393 EXPECT_TRUE(painter
->UseSoloWindowHeader());
395 // Create non-drawing window similar to DragDropTracker.
396 scoped_ptr
<aura::Window
> window(new aura::Window(NULL
));
397 window
->SetType(aura::client::WINDOW_TYPE_NORMAL
);
398 window
->Init(ui::LAYER_NOT_DRAWN
);
399 aura::client::ParentWindowWithContext(window
.get(), widget
->GetNativeWindow(),
403 // Despite two windows, the first window should still be considered "solo"
404 // because non-drawing windows aren't included in the computation.
405 EXPECT_TRUE(painter
->UseSoloWindowHeader());
409 // Multiple displays are not supported on Windows Ash. http://crbug.com/165962
410 #define MAYBE_UseSoloWindowHeaderMultiDisplay \
411 DISABLED_UseSoloWindowHeaderMultiDisplay
413 #define MAYBE_UseSoloWindowHeaderMultiDisplay \
414 UseSoloWindowHeaderMultiDisplay
417 TEST_F(HeaderPainterTest
, MAYBE_UseSoloWindowHeaderMultiDisplay
) {
418 if (!SupportsMultipleDisplays())
421 UpdateDisplay("1000x600,600x400");
423 // Create two widgets and painters for them.
424 scoped_ptr
<Widget
> w1(CreateTestWidget());
425 scoped_ptr
<HeaderPainter
> p1(CreateTestPainter(w1
.get()));
426 w1
->SetBounds(gfx::Rect(0, 0, 100, 100));
428 WindowRepaintChecker
checker1(w1
->GetNativeWindow());
429 scoped_ptr
<Widget
> w2(CreateTestWidget());
430 scoped_ptr
<HeaderPainter
> p2(CreateTestPainter(w2
.get()));
431 w2
->SetBounds(gfx::Rect(0, 0, 100, 100));
433 WindowRepaintChecker
checker2(w2
->GetNativeWindow());
435 // Now there are two windows in the same display, so we should not use solo
437 EXPECT_FALSE(p1
->UseSoloWindowHeader());
438 EXPECT_FALSE(p2
->UseSoloWindowHeader());
439 EXPECT_TRUE(checker1
.IsPaintScheduledAndReset());
441 // Moves the second window to the secondary display. Both w1/w2 should be
443 w2
->SetBounds(gfx::Rect(1200, 0, 100, 100));
444 EXPECT_TRUE(p1
->UseSoloWindowHeader());
445 EXPECT_TRUE(p2
->UseSoloWindowHeader());
446 EXPECT_TRUE(checker1
.IsPaintScheduledAndReset());
447 EXPECT_TRUE(checker2
.IsPaintScheduledAndReset());
449 // Open two more windows in the primary display.
450 scoped_ptr
<Widget
> w3(CreateTestWidget());
451 scoped_ptr
<HeaderPainter
> p3(CreateTestPainter(w3
.get()));
452 w3
->SetBounds(gfx::Rect(0, 0, 100, 100));
454 scoped_ptr
<Widget
> w4(CreateTestWidget());
455 scoped_ptr
<HeaderPainter
> p4(CreateTestPainter(w4
.get()));
456 w4
->SetBounds(gfx::Rect(0, 0, 100, 100));
459 // Because the primary display has two windows w1 and w3, they shouldn't be
460 // solo. w2 should be solo.
461 EXPECT_FALSE(p1
->UseSoloWindowHeader());
462 EXPECT_TRUE(p2
->UseSoloWindowHeader());
463 EXPECT_FALSE(p3
->UseSoloWindowHeader());
464 EXPECT_FALSE(p4
->UseSoloWindowHeader());
465 EXPECT_TRUE(checker1
.IsPaintScheduledAndReset());
467 // Moves the w4 to the secondary display. Now the w2 shouldn't be solo
469 w4
->SetBounds(gfx::Rect(1200, 0, 100, 100));
470 EXPECT_FALSE(p1
->UseSoloWindowHeader());
471 EXPECT_FALSE(p2
->UseSoloWindowHeader());
472 EXPECT_FALSE(p3
->UseSoloWindowHeader());
473 EXPECT_FALSE(p4
->UseSoloWindowHeader());
474 EXPECT_TRUE(checker2
.IsPaintScheduledAndReset());
476 // Moves the w3 to the secondary display too. Now w1 should be solo again.
477 w3
->SetBounds(gfx::Rect(1200, 0, 100, 100));
478 EXPECT_TRUE(p1
->UseSoloWindowHeader());
479 EXPECT_FALSE(p2
->UseSoloWindowHeader());
480 EXPECT_FALSE(p3
->UseSoloWindowHeader());
481 EXPECT_FALSE(p4
->UseSoloWindowHeader());
482 EXPECT_TRUE(checker1
.IsPaintScheduledAndReset());
484 // Change the w3 state to maximize. Doesn't affect to w1.
485 wm::GetWindowState(w3
->GetNativeWindow())->Maximize();
486 EXPECT_TRUE(p1
->UseSoloWindowHeader());
487 EXPECT_FALSE(p2
->UseSoloWindowHeader());
488 EXPECT_FALSE(p3
->UseSoloWindowHeader());
489 EXPECT_FALSE(p4
->UseSoloWindowHeader());
491 // Close the w3 and w4.
494 EXPECT_TRUE(p1
->UseSoloWindowHeader());
495 EXPECT_TRUE(p2
->UseSoloWindowHeader());
496 EXPECT_TRUE(checker2
.IsPaintScheduledAndReset());
498 // Move w2 back to the primary display.
499 w2
->SetBounds(gfx::Rect(0, 0, 100, 100));
500 EXPECT_FALSE(p1
->UseSoloWindowHeader());
501 EXPECT_FALSE(p2
->UseSoloWindowHeader());
502 EXPECT_TRUE(checker1
.IsPaintScheduledAndReset());
503 EXPECT_TRUE(checker2
.IsPaintScheduledAndReset());
507 EXPECT_TRUE(p1
->UseSoloWindowHeader());
508 EXPECT_TRUE(checker1
.IsPaintScheduledAndReset());
511 TEST_F(HeaderPainterTest
, GetHeaderOpacity
) {
512 // Create a widget and a painter for it.
513 scoped_ptr
<Widget
> w1(CreateTestWidget());
514 scoped_ptr
<HeaderPainter
> p1(CreateTestPainter(w1
.get()));
517 // Modify the values of the opacity constants so that they each have a
519 ScopedOpacityConstantModifier opacity_constant_modifier
;
521 // Solo active window has solo window opacity.
522 EXPECT_EQ(HeaderPainter::kSoloWindowOpacity
,
523 p1
->GetHeaderOpacity(HeaderPainter::ACTIVE
,
524 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE
,
527 // Create a second widget and painter.
528 scoped_ptr
<Widget
> w2(CreateTestWidget());
529 scoped_ptr
<HeaderPainter
> p2(CreateTestPainter(w2
.get()));
532 // Active window has active window opacity.
533 EXPECT_EQ(HeaderPainter::kActiveWindowOpacity
,
534 p2
->GetHeaderOpacity(HeaderPainter::ACTIVE
,
535 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE
,
538 // Inactive window has inactive window opacity.
539 EXPECT_EQ(HeaderPainter::kInactiveWindowOpacity
,
540 p2
->GetHeaderOpacity(HeaderPainter::INACTIVE
,
541 IDR_AURA_WINDOW_HEADER_BASE_INACTIVE
,
544 // Regular maximized windows are fully opaque.
545 wm::GetWindowState(w1
->GetNativeWindow())->Maximize();
547 p1
->GetHeaderOpacity(HeaderPainter::ACTIVE
,
548 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE
,
552 // Test that the minimal header style is used in the proper situations.
553 TEST_F(HeaderPainterTest
, MinimalHeaderStyle
) {
554 // Create a widget and a painter for it.
555 scoped_ptr
<Widget
> w(CreateTestWidget());
556 scoped_ptr
<HeaderPainter
> p(CreateTestPainter(w
.get()));
559 // Regular non-maximized windows should not use the minimal header style.
560 EXPECT_FALSE(p
->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO
));
562 // Regular maximized windows should use the minimal header style.
564 EXPECT_TRUE(p
->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO
));
566 // Test cases where the maximized window should not use the minimal header
568 EXPECT_FALSE(p
->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_YES
));
570 wm::GetWindowState(w
->GetNativeWindow())->SetTrackedByWorkspace(false);
571 EXPECT_FALSE(p
->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO
));
572 wm::GetWindowState(w
->GetNativeWindow())->SetTrackedByWorkspace(true);
575 // Ensure the title text is vertically aligned with the window icon.
576 TEST_F(HeaderPainterTest
, TitleIconAlignment
) {
577 scoped_ptr
<Widget
> w(CreateTestWidget());
579 ash::FrameCaptionButtonContainerView
container(w
.get(),
580 ash::FrameCaptionButtonContainerView::MINIMIZE_ALLOWED
);
581 views::View window_icon
;
582 window_icon
.SetBounds(0, 0, 16, 16);
584 w
->non_client_view()->frame_view(),
587 w
->SetBounds(gfx::Rect(0, 0, 500, 500));
590 // Title and icon are aligned when shorter_header is false.
591 p
.LayoutHeader(false);
592 gfx::Font default_font
;
593 gfx::Rect large_header_title_bounds
= p
.GetTitleBounds(default_font
);
594 EXPECT_EQ(window_icon
.bounds().CenterPoint().y(),
595 large_header_title_bounds
.CenterPoint().y());
597 // Title and icon are aligned when shorter_header is true.
598 p
.LayoutHeader(true);
599 gfx::Rect short_header_title_bounds
= p
.GetTitleBounds(default_font
);
600 EXPECT_EQ(window_icon
.bounds().CenterPoint().y(),
601 short_header_title_bounds
.CenterPoint().y());
604 TEST_F(HeaderPainterTest
, ChildWindowVisibility
) {
605 scoped_ptr
<Widget
> w1(CreateTestWidget());
606 scoped_ptr
<HeaderPainter
> p1(CreateTestPainter(w1
.get()));
609 // Solo active window has solo window opacity.
610 EXPECT_EQ(HeaderPainter::kSoloWindowOpacity
,
611 p1
->GetHeaderOpacity(HeaderPainter::ACTIVE
,
612 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE
,
615 // Create a child window which doesn't affect the solo header.
616 scoped_ptr
<Widget
> w2(new Widget
);
617 Widget::InitParams
params(Widget::InitParams::TYPE_CONTROL
);
618 params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
619 params
.parent
= w1
->GetNativeView();
623 // Still has solo header if child window is added.
624 EXPECT_EQ(HeaderPainter::kSoloWindowOpacity
,
625 p1
->GetHeaderOpacity(HeaderPainter::ACTIVE
,
626 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE
,
629 // Change the visibility of w2 and verifies w1 still has solo header.
631 EXPECT_EQ(HeaderPainter::kSoloWindowOpacity
,
632 p1
->GetHeaderOpacity(HeaderPainter::ACTIVE
,
633 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE
,
637 TEST_F(HeaderPainterTest
, NoCrashShutdownWithAlwaysOnTopWindow
) {
638 // Create normal window and an always-on-top window, and leave it as is
639 // and finish the test, then verify it doesn't cause a crash. See
640 // crbug.com/273310. Note that those widgets will be deleted at
641 // RootWindowController::CloseChildWindows(), so this code is memory-safe.
642 Widget
* w1
= new Widget
;
643 Widget::InitParams params1
;
644 params1
.context
= CurrentContext();
646 HeaderPainterOwner
* o1
= new HeaderPainterOwner(w1
);
647 HeaderPainter
* p1
= o1
->header_painter();
648 w1
->SetBounds(gfx::Rect(0, 0, 100, 100));
650 EXPECT_TRUE(p1
->UseSoloWindowHeader());
652 Widget
* w2
= new Widget
;
653 Widget::InitParams params2
;
654 params2
.context
= CurrentContext();
655 params2
.keep_on_top
= true;
657 HeaderPainterOwner
* o2
= new HeaderPainterOwner(w2
);
658 HeaderPainter
* p2
= o2
->header_painter();
660 EXPECT_FALSE(p1
->UseSoloWindowHeader());
661 EXPECT_FALSE(p2
->UseSoloWindowHeader());
663 // Exit with no resource release. They'll be released at shutdown.