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. 4 5#include "ash/wm/frame_painter.h" 6 7#include "ash/ash_constants.h" 8#include "ash/shell.h" 9#include "ash/shell_window_ids.h" 10#include "ash/test/ash_test_base.h" 11#include "ash/wm/property_util.h" 12#include "ash/wm/window_properties.h" 13#include "ash/wm/window_util.h" 14#include "base/memory/scoped_ptr.h" 15#include "base/message_loop/message_loop.h" 16#include "grit/ash_resources.h" 17#include "testing/gtest/include/gtest/gtest.h" 18#include "ui/aura/client/aura_constants.h" 19#include "ui/aura/root_window.h" 20#include "ui/aura/window_observer.h" 21#include "ui/base/hit_test.h" 22#include "ui/base/theme_provider.h" 23#include "ui/gfx/font.h" 24#include "ui/gfx/screen.h" 25#include "ui/views/controls/button/button.h" 26#include "ui/views/controls/button/image_button.h" 27#include "ui/views/widget/widget.h" 28#include "ui/views/widget/widget_delegate.h" 29#include "ui/views/widget/widget_observer.h" 30#include "ui/views/window/non_client_view.h" 31 32using ash::FramePainter; 33using ui::ThemeProvider; 34using views::Button; 35using views::ImageButton; 36using views::NonClientFrameView; 37using views::ToggleImageButton; 38using views::Widget; 39 40namespace { 41 42bool ImagesMatch(ImageButton* button, 43 int normal_image_id, 44 int hovered_image_id, 45 int pressed_image_id) { 46 ThemeProvider* theme = button->GetWidget()->GetThemeProvider(); 47 gfx::ImageSkia* normal = theme->GetImageSkiaNamed(normal_image_id); 48 gfx::ImageSkia* hovered = theme->GetImageSkiaNamed(hovered_image_id); 49 gfx::ImageSkia* pressed = theme->GetImageSkiaNamed(pressed_image_id); 50 return button->GetImage(Button::STATE_NORMAL).BackedBySameObjectAs(*normal) && 51 button->GetImage(Button::STATE_HOVERED).BackedBySameObjectAs(*hovered) && 52 button->GetImage(Button::STATE_PRESSED).BackedBySameObjectAs(*pressed); 53} 54 55class ResizableWidgetDelegate : public views::WidgetDelegate { 56 public: 57 ResizableWidgetDelegate(views::Widget* widget) { 58 widget_ = widget; 59 } 60 61 virtual bool CanResize() const OVERRIDE { return true; } 62 // Implementations of the widget class. 63 virtual views::Widget* GetWidget() OVERRIDE { return widget_; } 64 virtual const views::Widget* GetWidget() const OVERRIDE { return widget_; } 65 virtual void DeleteDelegate() OVERRIDE { 66 delete this; 67 } 68 69 private: 70 views::Widget* widget_; 71 72 DISALLOW_COPY_AND_ASSIGN(ResizableWidgetDelegate); 73}; 74 75class WindowRepaintChecker : public aura::WindowObserver { 76 public: 77 explicit WindowRepaintChecker(aura::Window* window) 78 : is_paint_scheduled_(false) { 79 window->AddObserver(this); 80 } 81 virtual ~WindowRepaintChecker() { 82 } 83 84 bool IsPaintScheduledAndReset() { 85 bool result = is_paint_scheduled_; 86 is_paint_scheduled_ = false; 87 return result; 88 } 89 90 private: 91 // aura::WindowObserver overrides: 92 virtual void OnWindowPaintScheduled(aura::Window* window, 93 const gfx::Rect& region) OVERRIDE { 94 is_paint_scheduled_ = true; 95 } 96 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { 97 window->RemoveObserver(this); 98 } 99 100 bool is_paint_scheduled_; 101 102 DISALLOW_COPY_AND_ASSIGN(WindowRepaintChecker); 103}; 104 105// Modifies the values of kInactiveWindowOpacity, kActiveWindowOpacity, and 106// kSoloWindowOpacity for the lifetime of the class. This is useful so that 107// the constants each have different values. 108class ScopedOpacityConstantModifier { 109 public: 110 ScopedOpacityConstantModifier() 111 : initial_active_window_opacity_( 112 ash::FramePainter::kActiveWindowOpacity), 113 initial_inactive_window_opacity_( 114 ash::FramePainter::kInactiveWindowOpacity), 115 initial_solo_window_opacity_(ash::FramePainter::kSoloWindowOpacity) { 116 ash::FramePainter::kActiveWindowOpacity = 100; 117 ash::FramePainter::kInactiveWindowOpacity = 120; 118 ash::FramePainter::kSoloWindowOpacity = 140; 119 } 120 ~ScopedOpacityConstantModifier() { 121 ash::FramePainter::kActiveWindowOpacity = initial_active_window_opacity_; 122 ash::FramePainter::kInactiveWindowOpacity = 123 initial_inactive_window_opacity_; 124 ash::FramePainter::kSoloWindowOpacity = initial_solo_window_opacity_; 125 } 126 127 private: 128 int initial_active_window_opacity_; 129 int initial_inactive_window_opacity_; 130 int initial_solo_window_opacity_; 131 132 DISALLOW_COPY_AND_ASSIGN(ScopedOpacityConstantModifier); 133}; 134 135// Creates a new FramePainter with empty buttons. Caller owns the memory. 136FramePainter* CreateTestPainter(Widget* widget) { 137 FramePainter* painter = new FramePainter(); 138 ImageButton* size_button = new ImageButton(NULL); 139 ImageButton* close_button = new ImageButton(NULL); 140 // Add the buttons to the widget's non-client frame view so they will be 141 // deleted when the widget is destroyed. 142 NonClientFrameView* frame_view = widget->non_client_view()->frame_view(); 143 frame_view->AddChildView(size_button); 144 frame_view->AddChildView(close_button); 145 painter->Init(widget, 146 NULL, 147 size_button, 148 close_button, 149 FramePainter::SIZE_BUTTON_MAXIMIZES); 150 return painter; 151} 152 153// Self-owned manager of the frame painter which deletes the painter and itself 154// when its widget is closed. 155class FramePainterOwner : views::WidgetObserver { 156 public: 157 explicit FramePainterOwner(Widget* widget) 158 : frame_painter_(CreateTestPainter(widget)) { 159 widget->AddObserver(this); 160 } 161 162 virtual ~FramePainterOwner() {} 163 164 FramePainter* frame_painter() { return frame_painter_.get(); } 165 166 private: 167 virtual void OnWidgetDestroying(Widget* widget) OVERRIDE { 168 widget->RemoveObserver(this); 169 // Do not delete directly here, since the task of FramePainter causing 170 // the crash of crbug.com/273310 may run after this class handles this 171 // event. 172 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 173 } 174 175 scoped_ptr<FramePainter> frame_painter_; 176 177 DISALLOW_COPY_AND_ASSIGN(FramePainterOwner); 178}; 179 180} // namespace 181 182namespace ash { 183 184class FramePainterTest : public ash::test::AshTestBase { 185 public: 186 // Creates a test widget that owns its native widget. 187 Widget* CreateTestWidget() { 188 Widget* widget = new Widget; 189 Widget::InitParams params; 190 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 191 params.context = CurrentContext(); 192 widget->Init(params); 193 return widget; 194 } 195 196 Widget* CreateAlwaysOnTopWidget() { 197 Widget* widget = new Widget; 198 Widget::InitParams params; 199 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 200 params.context = CurrentContext(); 201 params.keep_on_top = true; 202 widget->Init(params); 203 return widget; 204 } 205 206 Widget* CreatePanelWidget() { 207 Widget* widget = new Widget; 208 Widget::InitParams params; 209 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 210 params.context = CurrentContext(); 211 params.type = Widget::InitParams::TYPE_PANEL; 212 widget->Init(params); 213 return widget; 214 } 215 216 Widget* CreateResizableWidget() { 217 Widget* widget = new Widget; 218 Widget::InitParams params; 219 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 220 params.context = CurrentContext(); 221 params.keep_on_top = true; 222 params.delegate = new ResizableWidgetDelegate(widget); 223 params.type = Widget::InitParams::TYPE_WINDOW; 224 widget->Init(params); 225 return widget; 226 } 227}; 228 229TEST_F(FramePainterTest, CreateAndDeleteSingleWindow) { 230 // Ensure that creating/deleting a window works well and doesn't cause 231 // crashes. See crbug.com/155634 232 aura::RootWindow* root = Shell::GetActiveRootWindow(); 233 234 scoped_ptr<Widget> widget(CreateTestWidget()); 235 scoped_ptr<FramePainter> painter(CreateTestPainter(widget.get())); 236 widget->Show(); 237 238 // We only have one window, so it should use a solo header. 239 EXPECT_TRUE(painter->UseSoloWindowHeader()); 240 EXPECT_TRUE(root->GetProperty(internal::kSoloWindowHeaderKey)); 241 242 // Close the window. 243 widget.reset(); 244 EXPECT_FALSE(root->GetProperty(internal::kSoloWindowHeaderKey)); 245 246 // Recreate another window again. 247 widget.reset(CreateTestWidget()); 248 painter.reset(CreateTestPainter(widget.get())); 249 widget->Show(); 250 EXPECT_TRUE(painter->UseSoloWindowHeader()); 251 EXPECT_TRUE(root->GetProperty(internal::kSoloWindowHeaderKey)); 252} 253 254TEST_F(FramePainterTest, LayoutHeader) { 255 scoped_ptr<Widget> widget(CreateTestWidget()); 256 ImageButton size_button(NULL); 257 ImageButton close_button(NULL); 258 NonClientFrameView* frame_view = widget->non_client_view()->frame_view(); 259 frame_view->AddChildView(&size_button); 260 frame_view->AddChildView(&close_button); 261 scoped_ptr<FramePainter> painter(new FramePainter); 262 painter->Init(widget.get(), 263 NULL, 264 &size_button, 265 &close_button, 266 FramePainter::SIZE_BUTTON_MAXIMIZES); 267 widget->Show(); 268 269 // Basic layout. 270 painter->LayoutHeader(frame_view, false); 271 EXPECT_TRUE(ImagesMatch(&close_button, 272 IDR_AURA_WINDOW_CLOSE, 273 IDR_AURA_WINDOW_CLOSE_H, 274 IDR_AURA_WINDOW_CLOSE_P)); 275 EXPECT_TRUE(ImagesMatch(&size_button, 276 IDR_AURA_WINDOW_MAXIMIZE, 277 IDR_AURA_WINDOW_MAXIMIZE_H, 278 IDR_AURA_WINDOW_MAXIMIZE_P)); 279 280 // Shorter layout. 281 painter->LayoutHeader(frame_view, true); 282 EXPECT_TRUE(ImagesMatch(&close_button, 283 IDR_AURA_WINDOW_MAXIMIZED_CLOSE, 284 IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H, 285 IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P)); 286 EXPECT_TRUE(ImagesMatch(&size_button, 287 IDR_AURA_WINDOW_MAXIMIZED_RESTORE, 288 IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H, 289 IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P)); 290 291 // Maximized shorter layout. 292 widget->Maximize(); 293 painter->LayoutHeader(frame_view, true); 294 EXPECT_TRUE(ImagesMatch(&close_button, 295 IDR_AURA_WINDOW_MAXIMIZED_CLOSE2, 296 IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H, 297 IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P)); 298 EXPECT_TRUE(ImagesMatch(&size_button, 299 IDR_AURA_WINDOW_MAXIMIZED_RESTORE2, 300 IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H, 301 IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P)); 302 303 // Fullscreen can show the buttons during an immersive reveal, so it should 304 // use the same images as maximized. 305 widget->SetFullscreen(true); 306 painter->LayoutHeader(frame_view, true); 307 EXPECT_TRUE(ImagesMatch(&close_button, 308 IDR_AURA_WINDOW_MAXIMIZED_CLOSE2, 309 IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H, 310 IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P)); 311 EXPECT_TRUE(ImagesMatch(&size_button, 312 IDR_AURA_WINDOW_MAXIMIZED_RESTORE2, 313 IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H, 314 IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P)); 315} 316 317TEST_F(FramePainterTest, UseSoloWindowHeader) { 318 // Create a widget and a painter for it. 319 scoped_ptr<Widget> w1(CreateTestWidget()); 320 scoped_ptr<FramePainter> p1(CreateTestPainter(w1.get())); 321 w1->Show(); 322 323 // We only have one window, so it should use a solo header. 324 EXPECT_TRUE(p1->UseSoloWindowHeader()); 325 326 // Create a second widget and painter. 327 scoped_ptr<Widget> w2(CreateTestWidget()); 328 scoped_ptr<FramePainter> p2(CreateTestPainter(w2.get())); 329 w2->Show(); 330 331 // Now there are two windows, so we should not use solo headers. This only 332 // needs to test |p1| because "solo window headers" are a per-root-window 333 // property. 334 EXPECT_FALSE(p1->UseSoloWindowHeader()); 335 336 // Hide one window. Solo should be enabled. 337 w2->Hide(); 338 EXPECT_TRUE(p1->UseSoloWindowHeader()); 339 340 // Show that window. Solo should be disabled. 341 w2->Show(); 342 EXPECT_FALSE(p1->UseSoloWindowHeader()); 343 344 // Minimize the second window. Solo should be enabled. 345 w2->Minimize(); 346 EXPECT_TRUE(p1->UseSoloWindowHeader()); 347 348 // Close the minimized window. 349 w2.reset(); 350 EXPECT_TRUE(p1->UseSoloWindowHeader()); 351 352 // Open an always-on-top widget (which lives in a different container). 353 scoped_ptr<Widget> w3(CreateAlwaysOnTopWidget()); 354 scoped_ptr<FramePainter> p3(CreateTestPainter(w3.get())); 355 w3->Show(); 356 EXPECT_FALSE(p3->UseSoloWindowHeader()); 357 358 // Close the always-on-top widget. 359 w3.reset(); 360 EXPECT_TRUE(p1->UseSoloWindowHeader()); 361} 362 363// An open V2 app window should cause browser windows not to use the 364// solo window header. 365TEST_F(FramePainterTest, UseSoloWindowHeaderWithApp) { 366 // Create a widget and a painter for it. 367 scoped_ptr<Widget> w1(CreateTestWidget()); 368 scoped_ptr<FramePainter> p1(CreateTestPainter(w1.get())); 369 w1->Show(); 370 371 // We only have one window, so it should use a solo header. 372 EXPECT_TRUE(p1->UseSoloWindowHeader()); 373 374 // Simulate a V2 app window, which is part of the active workspace but does 375 // not have a frame painter. 376 scoped_ptr<Widget> w2(CreateTestWidget()); 377 w2->Show(); 378 379 // Now there are two windows, so we should not use solo headers. 380 EXPECT_FALSE(p1->UseSoloWindowHeader()); 381 382 // Minimize the app window. The first window should go solo again. 383 w2->Minimize(); 384 EXPECT_TRUE(p1->UseSoloWindowHeader()); 385 386 // Restoring the app window turns off solo headers. 387 w2->Restore(); 388 EXPECT_FALSE(p1->UseSoloWindowHeader()); 389} 390 391// Panels should not "count" for computing solo window headers, and the panel 392// itself should always have an opaque header. 393TEST_F(FramePainterTest, UseSoloWindowHeaderWithPanel) { 394 // Create a widget and a painter for it. 395 scoped_ptr<Widget> w1(CreateTestWidget()); 396 scoped_ptr<FramePainter> p1(CreateTestPainter(w1.get())); 397 w1->Show(); 398 399 // We only have one window, so it should use a solo header. 400 EXPECT_TRUE(p1->UseSoloWindowHeader()); 401 402 // Create a panel and a painter for it. 403 scoped_ptr<Widget> w2(CreatePanelWidget()); 404 scoped_ptr<FramePainter> p2(CreateTestPainter(w2.get())); 405 w2->Show(); 406 407 // Despite two windows, the first window should still be considered "solo" 408 // because panels aren't included in the computation. 409 EXPECT_TRUE(p1->UseSoloWindowHeader()); 410 411 // The panel itself is not considered solo. 412 EXPECT_FALSE(p2->UseSoloWindowHeader()); 413 414 // Even after closing the first window, the panel is still not considered 415 // solo. 416 w1.reset(); 417 EXPECT_FALSE(p2->UseSoloWindowHeader()); 418} 419 420// Modal dialogs should not use solo headers. 421TEST_F(FramePainterTest, UseSoloWindowHeaderModal) { 422 // Create a widget and a painter for it. 423 scoped_ptr<Widget> w1(CreateTestWidget()); 424 scoped_ptr<FramePainter> p1(CreateTestPainter(w1.get())); 425 w1->Show(); 426 427 // We only have one window, so it should use a solo header. 428 EXPECT_TRUE(p1->UseSoloWindowHeader()); 429 430 // Create a fake modal window. 431 scoped_ptr<Widget> w2(CreateTestWidget()); 432 scoped_ptr<FramePainter> p2(CreateTestPainter(w2.get())); 433 w2->GetNativeWindow()->SetProperty(aura::client::kModalKey, 434 ui::MODAL_TYPE_WINDOW); 435 w2->Show(); 436 437 // Despite two windows, the first window should still be considered "solo" 438 // because modal windows aren't included in the computation. 439 EXPECT_TRUE(p1->UseSoloWindowHeader()); 440 441 // The modal window itself is not considered solo. 442 EXPECT_FALSE(p2->UseSoloWindowHeader()); 443} 444 445// Constrained windows should not use solo headers. 446TEST_F(FramePainterTest, UseSoloWindowHeaderConstrained) { 447 // Create a widget and a painter for it. 448 scoped_ptr<Widget> w1(CreateTestWidget()); 449 scoped_ptr<FramePainter> p1(CreateTestPainter(w1.get())); 450 w1->Show(); 451 452 // We only have one window, so it should use a solo header. 453 EXPECT_TRUE(p1->UseSoloWindowHeader()); 454 455 // Create a fake constrained window. 456 scoped_ptr<Widget> w2(CreateTestWidget()); 457 scoped_ptr<FramePainter> p2(CreateTestPainter(w2.get())); 458 w2->GetNativeWindow()->SetProperty(ash::kConstrainedWindowKey, true); 459 w2->Show(); 460 461 // Despite two windows, the first window should still be considered "solo" 462 // because constrained windows aren't included in the computation. 463 EXPECT_TRUE(p1->UseSoloWindowHeader()); 464 465 // The constrained window itself is not considered solo. 466 EXPECT_FALSE(p2->UseSoloWindowHeader()); 467} 468 469// Non-drawing windows should not affect the solo computation. 470TEST_F(FramePainterTest, UseSoloWindowHeaderNotDrawn) { 471 // Create a widget and a painter for it. 472 scoped_ptr<Widget> widget(CreateTestWidget()); 473 scoped_ptr<FramePainter> painter(CreateTestPainter(widget.get())); 474 widget->Show(); 475 476 // We only have one window, so it should use a solo header. 477 EXPECT_TRUE(painter->UseSoloWindowHeader()); 478 479 // Create non-drawing window similar to DragDropTracker. 480 scoped_ptr<aura::Window> window(new aura::Window(NULL)); 481 window->SetType(aura::client::WINDOW_TYPE_NORMAL); 482 window->Init(ui::LAYER_NOT_DRAWN); 483 window->SetDefaultParentByRootWindow( 484 widget->GetNativeWindow()->GetRootWindow(), gfx::Rect()); 485 window->Show(); 486 487 // Despite two windows, the first window should still be considered "solo" 488 // because non-drawing windows aren't included in the computation. 489 EXPECT_TRUE(painter->UseSoloWindowHeader()); 490} 491 492#if defined(OS_WIN) 493// Multiple displays are not supported on Windows Ash. http://crbug.com/165962 494#define MAYBE_UseSoloWindowHeaderMultiDisplay \ 495 DISABLED_UseSoloWindowHeaderMultiDisplay 496#else 497#define MAYBE_UseSoloWindowHeaderMultiDisplay \ 498 UseSoloWindowHeaderMultiDisplay 499#endif 500 501TEST_F(FramePainterTest, MAYBE_UseSoloWindowHeaderMultiDisplay) { 502 if (!SupportsMultipleDisplays()) 503 return; 504 505 UpdateDisplay("1000x600,600x400"); 506 507 // Create two widgets and painters for them. 508 scoped_ptr<Widget> w1(CreateTestWidget()); 509 scoped_ptr<FramePainter> p1(CreateTestPainter(w1.get())); 510 w1->SetBounds(gfx::Rect(0, 0, 100, 100)); 511 w1->Show(); 512 WindowRepaintChecker checker1(w1->GetNativeWindow()); 513 scoped_ptr<Widget> w2(CreateTestWidget()); 514 scoped_ptr<FramePainter> p2(CreateTestPainter(w2.get())); 515 w2->SetBounds(gfx::Rect(0, 0, 100, 100)); 516 w2->Show(); 517 WindowRepaintChecker checker2(w2->GetNativeWindow()); 518 519 // Now there are two windows in the same display, so we should not use solo 520 // headers. 521 EXPECT_FALSE(p1->UseSoloWindowHeader()); 522 EXPECT_FALSE(p2->UseSoloWindowHeader()); 523 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); 524 525 // Moves the second window to the secondary display. Both w1/w2 should be 526 // solo. 527 w2->SetBounds(gfx::Rect(1200, 0, 100, 100)); 528 EXPECT_TRUE(p1->UseSoloWindowHeader()); 529 EXPECT_TRUE(p2->UseSoloWindowHeader()); 530 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); 531 EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); 532 533 // Open two more windows in the primary display. 534 scoped_ptr<Widget> w3(CreateTestWidget()); 535 scoped_ptr<FramePainter> p3(CreateTestPainter(w3.get())); 536 w3->SetBounds(gfx::Rect(0, 0, 100, 100)); 537 w3->Show(); 538 scoped_ptr<Widget> w4(CreateTestWidget()); 539 scoped_ptr<FramePainter> p4(CreateTestPainter(w4.get())); 540 w4->SetBounds(gfx::Rect(0, 0, 100, 100)); 541 w4->Show(); 542 543 // Because the primary display has two windows w1 and w3, they shouldn't be 544 // solo. w2 should be solo. 545 EXPECT_FALSE(p1->UseSoloWindowHeader()); 546 EXPECT_TRUE(p2->UseSoloWindowHeader()); 547 EXPECT_FALSE(p3->UseSoloWindowHeader()); 548 EXPECT_FALSE(p4->UseSoloWindowHeader()); 549 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); 550 551 // Moves the w4 to the secondary display. Now the w2 shouldn't be solo 552 // anymore. 553 w4->SetBounds(gfx::Rect(1200, 0, 100, 100)); 554 EXPECT_FALSE(p1->UseSoloWindowHeader()); 555 EXPECT_FALSE(p2->UseSoloWindowHeader()); 556 EXPECT_FALSE(p3->UseSoloWindowHeader()); 557 EXPECT_FALSE(p4->UseSoloWindowHeader()); 558 EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); 559 560 // Moves the w3 to the secondary display too. Now w1 should be solo again. 561 w3->SetBounds(gfx::Rect(1200, 0, 100, 100)); 562 EXPECT_TRUE(p1->UseSoloWindowHeader()); 563 EXPECT_FALSE(p2->UseSoloWindowHeader()); 564 EXPECT_FALSE(p3->UseSoloWindowHeader()); 565 EXPECT_FALSE(p4->UseSoloWindowHeader()); 566 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); 567 568 // Change the w3 state to maximize. Doesn't affect to w1. 569 wm::MaximizeWindow(w3->GetNativeWindow()); 570 EXPECT_TRUE(p1->UseSoloWindowHeader()); 571 EXPECT_FALSE(p2->UseSoloWindowHeader()); 572 EXPECT_FALSE(p3->UseSoloWindowHeader()); 573 EXPECT_FALSE(p4->UseSoloWindowHeader()); 574 575 // Close the w3 and w4. 576 w3.reset(); 577 w4.reset(); 578 EXPECT_TRUE(p1->UseSoloWindowHeader()); 579 EXPECT_TRUE(p2->UseSoloWindowHeader()); 580 EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); 581 582 // Move w2 back to the primary display. 583 w2->SetBounds(gfx::Rect(0, 0, 100, 100)); 584 EXPECT_FALSE(p1->UseSoloWindowHeader()); 585 EXPECT_FALSE(p2->UseSoloWindowHeader()); 586 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); 587 EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); 588 589 // Close w2. 590 w2.reset(); 591 EXPECT_TRUE(p1->UseSoloWindowHeader()); 592 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); 593} 594 595TEST_F(FramePainterTest, GetHeaderOpacity) { 596 // Create a widget and a painter for it. 597 scoped_ptr<Widget> w1(CreateTestWidget()); 598 scoped_ptr<FramePainter> p1(CreateTestPainter(w1.get())); 599 w1->Show(); 600 601 // Modify the values of the opacity constants so that they each have a 602 // different value. 603 ScopedOpacityConstantModifier opacity_constant_modifier; 604 605 // Solo active window has solo window opacity. 606 EXPECT_EQ(FramePainter::kSoloWindowOpacity, 607 p1->GetHeaderOpacity(FramePainter::ACTIVE, 608 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE, 609 0)); 610 611 // Create a second widget and painter. 612 scoped_ptr<Widget> w2(CreateTestWidget()); 613 scoped_ptr<FramePainter> p2(CreateTestPainter(w2.get())); 614 w2->Show(); 615 616 // Active window has active window opacity. 617 EXPECT_EQ(FramePainter::kActiveWindowOpacity, 618 p2->GetHeaderOpacity(FramePainter::ACTIVE, 619 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE, 620 0)); 621 622 // Inactive window has inactive window opacity. 623 EXPECT_EQ(FramePainter::kInactiveWindowOpacity, 624 p2->GetHeaderOpacity(FramePainter::INACTIVE, 625 IDR_AURA_WINDOW_HEADER_BASE_INACTIVE, 626 0)); 627 628 // Regular maximized windows are fully opaque. 629 ash::wm::MaximizeWindow(w1->GetNativeWindow()); 630 EXPECT_EQ(255, 631 p1->GetHeaderOpacity(FramePainter::ACTIVE, 632 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE, 633 0)); 634} 635 636// Test that the minimal header style is used in the proper situations. 637TEST_F(FramePainterTest, MinimalHeaderStyle) { 638 // Create a widget and a painter for it. 639 scoped_ptr<Widget> w(CreateTestWidget()); 640 scoped_ptr<FramePainter> p(CreateTestPainter(w.get())); 641 w->Show(); 642 643 // Regular non-maximized windows should not use the minimal header style. 644 EXPECT_FALSE(p->ShouldUseMinimalHeaderStyle(FramePainter::THEMED_NO)); 645 646 // Regular maximized windows should use the minimal header style. 647 w->Maximize(); 648 EXPECT_TRUE(p->ShouldUseMinimalHeaderStyle(FramePainter::THEMED_NO)); 649 650 // Test cases where the maximized window should not use the minimal header 651 // style. 652 EXPECT_FALSE(p->ShouldUseMinimalHeaderStyle(FramePainter::THEMED_YES)); 653 654 SetTrackedByWorkspace(w->GetNativeWindow(), false); 655 EXPECT_FALSE(p->ShouldUseMinimalHeaderStyle(FramePainter::THEMED_NO)); 656 SetTrackedByWorkspace(w->GetNativeWindow(), true); 657} 658 659// Ensure the title text is vertically aligned with the window icon. 660TEST_F(FramePainterTest, TitleIconAlignment) { 661 scoped_ptr<Widget> w(CreateTestWidget()); 662 FramePainter p; 663 ImageButton size(NULL); 664 ImageButton close(NULL); 665 views::View window_icon; 666 window_icon.SetBounds(0, 0, 16, 16); 667 p.Init(w.get(), 668 &window_icon, 669 &size, 670 &close, 671 FramePainter::SIZE_BUTTON_MAXIMIZES); 672 w->SetBounds(gfx::Rect(0, 0, 500, 500)); 673 w->Show(); 674 675 // Title and icon are aligned when shorter_header is false. 676 p.LayoutHeader(w->non_client_view()->frame_view(), false); 677 gfx::Font default_font; 678 gfx::Rect large_header_title_bounds = p.GetTitleBounds(default_font); 679 EXPECT_EQ(window_icon.bounds().CenterPoint().y(), 680 large_header_title_bounds.CenterPoint().y()); 681 682 // Title and icon are aligned when shorter_header is true. 683 p.LayoutHeader(w->non_client_view()->frame_view(), true); 684 gfx::Rect short_header_title_bounds = p.GetTitleBounds(default_font); 685 EXPECT_EQ(window_icon.bounds().CenterPoint().y(), 686 short_header_title_bounds.CenterPoint().y()); 687} 688 689TEST_F(FramePainterTest, ChildWindowVisibility) { 690 scoped_ptr<Widget> w1(CreateTestWidget()); 691 scoped_ptr<FramePainter> p1(CreateTestPainter(w1.get())); 692 w1->Show(); 693 694 // Solo active window has solo window opacity. 695 EXPECT_EQ(FramePainter::kSoloWindowOpacity, 696 p1->GetHeaderOpacity(FramePainter::ACTIVE, 697 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE, 698 0)); 699 700 // Create a child window which doesn't affect the solo header. 701 scoped_ptr<Widget> w2(new Widget); 702 Widget::InitParams params(Widget::InitParams::TYPE_CONTROL); 703 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 704 params.parent = w1->GetNativeView(); 705 w2->Init(params); 706 w2->Show(); 707 708 // Still has solo header if child window is added. 709 EXPECT_EQ(FramePainter::kSoloWindowOpacity, 710 p1->GetHeaderOpacity(FramePainter::ACTIVE, 711 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE, 712 0)); 713 714 // Change the visibility of w2 and verifies w1 still has solo header. 715 w2->Hide(); 716 EXPECT_EQ(FramePainter::kSoloWindowOpacity, 717 p1->GetHeaderOpacity(FramePainter::ACTIVE, 718 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE, 719 0)); 720} 721 722TEST_F(FramePainterTest, NoCrashShutdownWithAlwaysOnTopWindow) { 723 // Create normal window and an always-on-top window, and leave it as is 724 // and finish the test, then verify it doesn't cause a crash. See 725 // crbug.com/273310. Note that those widgets will be deleted at 726 // RootWindowController::CloseChildWindows(), so this code is memory-safe. 727 Widget* w1 = new Widget; 728 Widget::InitParams params1; 729 params1.context = CurrentContext(); 730 w1->Init(params1); 731 FramePainterOwner* o1 = new FramePainterOwner(w1); 732 FramePainter* p1 = o1->frame_painter(); 733 w1->SetBounds(gfx::Rect(0, 0, 100, 100)); 734 w1->Show(); 735 EXPECT_TRUE(p1->UseSoloWindowHeader()); 736 737 Widget* w2 = new Widget; 738 Widget::InitParams params2; 739 params2.context = CurrentContext(); 740 params2.keep_on_top = true; 741 w2->Init(params2); 742 FramePainterOwner* o2 = new FramePainterOwner(w2); 743 FramePainter* p2 = o2->frame_painter(); 744 w2->Show(); 745 EXPECT_FALSE(p1->UseSoloWindowHeader()); 746 EXPECT_FALSE(p2->UseSoloWindowHeader()); 747 748 // Exit with no resource release. They'll be released at shutdown. 749} 750 751} // namespace ash 752