// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/i18n/time_formatting.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/panels/base_panel_browser_test.h" #include "chrome/browser/ui/panels/docked_panel_strip.h" #include "chrome/browser/ui/panels/panel.h" #include "chrome/browser/ui/panels/panel_bounds_animation.h" #include "chrome/browser/ui/panels/panel_browser_frame_view.h" #include "chrome/browser/ui/panels/panel_browser_view.h" #include "chrome/browser/ui/panels/panel_manager.h" #include "chrome/browser/web_applications/web_app.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension.h" #include "chrome/test/base/ui_test_utils.h" #include "grit/generated_resources.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/animation/linear_animation.h" #include "ui/base/l10n/l10n_util.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/menu_button.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" #include "ui/views/controls/link.h" #include "ui/views/controls/textfield/textfield.h" class PanelBrowserViewTest : public BasePanelBrowserTest { public: PanelBrowserViewTest() : BasePanelBrowserTest() { } protected: PanelBrowserView* GetBrowserView(Panel* panel) const { return static_cast<PanelBrowserView*>(panel->native_panel()); } gfx::Rect GetViewBounds(Panel* panel) const { return GetBrowserView(panel)->GetBounds(); } void SetViewBounds(Panel* panel, const gfx::Rect& rect) const { return GetBrowserView(panel)->SetPanelBounds(rect); } gfx::NativeWindow GetNativeWindow(Panel* panel) const { return GetBrowserView(panel)->GetNativeHandle(); } PanelBoundsAnimation* GetBoundsAnimator(Panel* panel) const { return GetBrowserView(panel)->bounds_animator_.get(); } int GetTitlebarHeight(Panel* panel) const { PanelBrowserFrameView* frame_view = GetBrowserView(panel)->GetFrameView(); return frame_view->NonClientTopBorderHeight() - frame_view->NonClientBorderThickness(); } PanelBrowserFrameView::PaintState GetTitlebarPaintState(Panel* panel) const { return GetBrowserView(panel)->GetFrameView()->paint_state_; } bool IsTitlebarPaintedAsActive(Panel* panel) const { return GetTitlebarPaintState(panel) == PanelBrowserFrameView::PAINT_AS_ACTIVE; } bool IsTitlebarPaintedAsInactive(Panel* panel) const { return GetTitlebarPaintState(panel) == PanelBrowserFrameView::PAINT_AS_INACTIVE; } bool IsTitlebarPaintedForAttention(Panel* panel) const { return GetTitlebarPaintState(panel) == PanelBrowserFrameView::PAINT_FOR_ATTENTION; } int GetControlCount(Panel* panel) const { return GetBrowserView(panel)->GetFrameView()->child_count(); } TabIconView* GetTitleIcon(Panel* panel) const { return GetBrowserView(panel)->GetFrameView()->title_icon_; } views::Label* GetTitleText(Panel* panel) const { return GetBrowserView(panel)->GetFrameView()->title_label_; } views::Button* GetCloseButton(Panel* panel) const { return GetBrowserView(panel)->GetFrameView()->close_button_; } views::Button* GetMinimizeButton(Panel* panel) const { return GetBrowserView(panel)->GetFrameView()->minimize_button_; } views::Button* GetRestoreButton(Panel* panel) const { return GetBrowserView(panel)->GetFrameView()->restore_button_; } bool ContainsControl(Panel* panel, views::View* control) const { return GetBrowserView(panel)->GetFrameView()->Contains(control); } void WaitTillBoundsAnimationFinished(Panel* panel) { // The timer for the animation will only kick in as async task. while (GetBoundsAnimator(panel)->is_animating()) { MessageLoopForUI::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); MessageLoopForUI::current()->RunAllPending(); } } void ClosePanelAndWaitForNotification(Panel* panel) { ui_test_utils::WindowedNotificationObserver signal( chrome::NOTIFICATION_BROWSER_CLOSED, content::Source<Browser>(panel->browser())); panel->Close(); signal.Wait(); } // We put all the testing logic in this class instead of the test so that // we do not need to declare each new test as a friend of PanelBrowserView // for the purpose of accessing its private members. void TestMinimizeAndRestore(bool enable_auto_hiding) { PanelManager* panel_manager = PanelManager::GetInstance(); DockedPanelStrip* docked_strip = panel_manager->docked_strip(); int expected_bottom_on_expanded = docked_strip->display_area().bottom(); int expected_bottom_on_title_only = expected_bottom_on_expanded; int expected_bottom_on_minimized = expected_bottom_on_expanded; // Turn on auto-hiding if requested. static const int bottom_thickness = 40; mock_display_settings_provider()->EnableAutoHidingDesktopBar( DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_BOTTOM, enable_auto_hiding, bottom_thickness); if (enable_auto_hiding) expected_bottom_on_title_only -= bottom_thickness; // Create and test one panel first. Panel* panel1 = CreatePanel("PanelTest1"); PanelBrowserView* browser_view1 = GetBrowserView(panel1); PanelBrowserFrameView* frame_view1 = browser_view1->GetFrameView(); // Test minimizing/restoring an individual panel. EXPECT_EQ(Panel::EXPANDED, panel1->expansion_state()); int initial_height = panel1->GetBounds().height(); panel1->SetExpansionState(Panel::MINIMIZED); EXPECT_EQ(Panel::MINIMIZED, panel1->expansion_state()); int titlebar_height = frame_view1->NonClientTopBorderHeight(); EXPECT_LT(panel1->GetBounds().height(), titlebar_height); EXPECT_GT(panel1->GetBounds().height(), 0); EXPECT_EQ(expected_bottom_on_minimized, panel1->GetBounds().bottom()); WaitTillBoundsAnimationFinished(panel1); EXPECT_FALSE(panel1->IsActive()); panel1->SetExpansionState(Panel::TITLE_ONLY); EXPECT_EQ(Panel::TITLE_ONLY, panel1->expansion_state()); EXPECT_EQ(titlebar_height, panel1->GetBounds().height()); EXPECT_EQ(expected_bottom_on_title_only, panel1->GetBounds().bottom()); WaitTillBoundsAnimationFinished(panel1); EXPECT_TRUE(frame_view1->close_button_->visible()); EXPECT_TRUE(frame_view1->title_icon_->visible()); EXPECT_TRUE(frame_view1->title_label_->visible()); panel1->SetExpansionState(Panel::EXPANDED); EXPECT_EQ(Panel::EXPANDED, panel1->expansion_state()); EXPECT_EQ(initial_height, panel1->GetBounds().height()); EXPECT_EQ(expected_bottom_on_expanded, panel1->GetBounds().bottom()); WaitTillBoundsAnimationFinished(panel1); EXPECT_TRUE(frame_view1->close_button_->visible()); EXPECT_TRUE(frame_view1->title_icon_->visible()); EXPECT_TRUE(frame_view1->title_label_->visible()); panel1->SetExpansionState(Panel::MINIMIZED); EXPECT_EQ(Panel::MINIMIZED, panel1->expansion_state()); EXPECT_LT(panel1->GetBounds().height(), titlebar_height); EXPECT_GT(panel1->GetBounds().height(), 0); EXPECT_EQ(expected_bottom_on_minimized, panel1->GetBounds().bottom()); panel1->SetExpansionState(Panel::TITLE_ONLY); EXPECT_EQ(Panel::TITLE_ONLY, panel1->expansion_state()); EXPECT_EQ(titlebar_height, panel1->GetBounds().height()); EXPECT_EQ(expected_bottom_on_title_only, panel1->GetBounds().bottom()); // Create 2 more panels for more testing. Panel* panel2 = CreatePanel("PanelTest2"); Panel* panel3 = CreatePanel("PanelTest3"); // Test bringing up or down the title-bar of all minimized panels. EXPECT_EQ(Panel::EXPANDED, panel2->expansion_state()); panel3->SetExpansionState(Panel::MINIMIZED); EXPECT_EQ(Panel::MINIMIZED, panel3->expansion_state()); mock_display_settings_provider()->SetDesktopBarVisibility( DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_BOTTOM, DisplaySettingsProvider::DESKTOP_BAR_VISIBLE); panel_manager->BringUpOrDownTitlebars(true); MessageLoopForUI::current()->RunAllPending(); EXPECT_EQ(Panel::TITLE_ONLY, panel1->expansion_state()); EXPECT_EQ(Panel::EXPANDED, panel2->expansion_state()); EXPECT_EQ(Panel::TITLE_ONLY, panel3->expansion_state()); mock_display_settings_provider()->SetDesktopBarVisibility( DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_BOTTOM, DisplaySettingsProvider::DESKTOP_BAR_HIDDEN); panel_manager->BringUpOrDownTitlebars(false); MessageLoopForUI::current()->RunAllPending(); EXPECT_EQ(Panel::MINIMIZED, panel1->expansion_state()); EXPECT_EQ(Panel::EXPANDED, panel2->expansion_state()); EXPECT_EQ(Panel::MINIMIZED, panel3->expansion_state()); // Test if it is OK to bring up title-bar given the mouse position. EXPECT_TRUE(panel_manager->ShouldBringUpTitlebars( panel1->GetBounds().x(), panel1->GetBounds().y())); EXPECT_FALSE(panel_manager->ShouldBringUpTitlebars( panel2->GetBounds().x(), panel2->GetBounds().y())); EXPECT_TRUE(panel_manager->ShouldBringUpTitlebars( panel3->GetBounds().right() - 1, panel3->GetBounds().bottom() - 1)); EXPECT_TRUE(panel_manager->ShouldBringUpTitlebars( panel3->GetBounds().right() - 1, panel3->GetBounds().bottom() + 10)); EXPECT_FALSE(panel_manager->ShouldBringUpTitlebars( 0, 0)); // Test that the panel in title-only state should not be minimized // regardless of the current mouse position when the panel is being dragged. panel1->SetExpansionState(Panel::TITLE_ONLY); EXPECT_FALSE(panel_manager->ShouldBringUpTitlebars( 0, 0)); browser_view1->OnTitlebarMousePressed(panel1->GetBounds().origin()); EXPECT_EQ(Panel::TITLE_ONLY, panel1->expansion_state()); browser_view1->OnTitlebarMouseDragged( panel1->GetBounds().origin().Subtract(gfx::Point(5, 5))); EXPECT_EQ(Panel::TITLE_ONLY, panel1->expansion_state()); EXPECT_TRUE(panel_manager->ShouldBringUpTitlebars( 0, 0)); browser_view1->OnTitlebarMouseReleased(panel::NO_MODIFIER); ClosePanelAndWaitForNotification(panel1); ClosePanelAndWaitForNotification(panel2); ClosePanelAndWaitForNotification(panel3); } void TestChangeAutoHideTaskBarThickness() { PanelManager* manager = PanelManager::GetInstance(); DockedPanelStrip* docked_strip = manager->docked_strip(); int initial_starting_right_position = docked_strip->StartingRightPosition(); int bottom_bar_thickness = 20; int right_bar_thickness = 30; mock_display_settings_provider()->EnableAutoHidingDesktopBar( DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_BOTTOM, true, bottom_bar_thickness); mock_display_settings_provider()->EnableAutoHidingDesktopBar( DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_RIGHT, true, right_bar_thickness); EXPECT_EQ( initial_starting_right_position - docked_strip->StartingRightPosition(), right_bar_thickness); Panel* panel = CreatePanel("PanelTest"); panel->SetExpansionState(Panel::TITLE_ONLY); WaitTillBoundsAnimationFinished(panel); EXPECT_EQ(docked_strip->display_area().bottom() - bottom_bar_thickness, panel->GetBounds().bottom()); EXPECT_EQ(docked_strip->StartingRightPosition(), panel->GetBounds().right()); initial_starting_right_position = docked_strip->StartingRightPosition(); int bottom_bar_thickness_delta = 10; bottom_bar_thickness += bottom_bar_thickness_delta; int right_bar_thickness_delta = 15; right_bar_thickness += right_bar_thickness_delta; mock_display_settings_provider()->SetDesktopBarThickness( DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_BOTTOM, bottom_bar_thickness); mock_display_settings_provider()->SetDesktopBarThickness( DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_RIGHT, right_bar_thickness); MessageLoopForUI::current()->RunAllPending(); EXPECT_EQ( initial_starting_right_position - docked_strip->StartingRightPosition(), right_bar_thickness_delta); EXPECT_EQ(docked_strip->display_area().bottom() - bottom_bar_thickness, panel->GetBounds().bottom()); EXPECT_EQ(docked_strip->StartingRightPosition(), panel->GetBounds().right()); initial_starting_right_position = docked_strip->StartingRightPosition(); bottom_bar_thickness_delta = 20; bottom_bar_thickness -= bottom_bar_thickness_delta; right_bar_thickness_delta = 10; right_bar_thickness -= right_bar_thickness_delta; mock_display_settings_provider()->SetDesktopBarThickness( DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_BOTTOM, bottom_bar_thickness); mock_display_settings_provider()->SetDesktopBarThickness( DisplaySettingsProvider::DESKTOP_BAR_ALIGNED_RIGHT, right_bar_thickness); MessageLoopForUI::current()->RunAllPending(); EXPECT_EQ( docked_strip->StartingRightPosition() - initial_starting_right_position, right_bar_thickness_delta); EXPECT_EQ(docked_strip->display_area().bottom() - bottom_bar_thickness, panel->GetBounds().bottom()); EXPECT_EQ(docked_strip->StartingRightPosition(), panel->GetBounds().right()); panel->Close(); } }; // Panel is not supported for Linux view yet. #if !defined(OS_LINUX) || !defined(TOOLKIT_VIEWS) IN_PROC_BROWSER_TEST_F(PanelBrowserViewTest, CreatePanelBasic) { CreatePanelParams params( "PanelTest", gfx::Rect(0, 0, 200, 150), SHOW_AS_ACTIVE); Panel* panel = CreatePanelWithParams(params); // Validate basic window properties. #if defined(OS_WIN) && !defined(USE_AURA) HWND native_window = GetNativeWindow(panel); RECT window_rect; EXPECT_TRUE(::GetWindowRect(native_window, &window_rect)); EXPECT_EQ(200, window_rect.right - window_rect.left); EXPECT_EQ(150, window_rect.bottom - window_rect.top); EXPECT_TRUE(::IsWindowVisible(native_window)); #endif panel->Close(); } IN_PROC_BROWSER_TEST_F(PanelBrowserViewTest, CreatePanelActive) { CreatePanelParams params("PanelTest", gfx::Rect(), SHOW_AS_ACTIVE); Panel* panel = CreatePanelWithParams(params); // Validate it is active. EXPECT_TRUE(panel->IsActive()); EXPECT_TRUE(IsTitlebarPaintedAsActive(panel)); // Validate window styles. We want to ensure that the window is created // with expected styles regardless of its active state. #if defined(OS_WIN) && !defined(USE_AURA) HWND native_window = GetNativeWindow(panel); LONG styles = ::GetWindowLong(native_window, GWL_STYLE); EXPECT_EQ(0, styles & WS_MAXIMIZEBOX); EXPECT_EQ(0, styles & WS_MINIMIZEBOX); LONG ext_styles = ::GetWindowLong(native_window, GWL_EXSTYLE); EXPECT_EQ(WS_EX_TOPMOST, ext_styles & WS_EX_TOPMOST); #endif panel->Close(); } IN_PROC_BROWSER_TEST_F(PanelBrowserViewTest, CreatePanelInactive) { CreatePanelParams params("PanelTest", gfx::Rect(), SHOW_AS_INACTIVE); Panel* panel = CreatePanelWithParams(params); // Validate it is inactive. EXPECT_FALSE(panel->IsActive()); EXPECT_FALSE(IsTitlebarPaintedAsActive(panel)); // Validate window styles. We want to ensure that the window is created // with expected styles regardless of its active state. #if defined(OS_WIN) && !defined(USE_AURA) HWND native_window = GetNativeWindow(panel); LONG styles = ::GetWindowLong(native_window, GWL_STYLE); EXPECT_EQ(0, styles & WS_MAXIMIZEBOX); EXPECT_EQ(0, styles & WS_MINIMIZEBOX); LONG ext_styles = ::GetWindowLong(native_window, GWL_EXSTYLE); EXPECT_EQ(WS_EX_TOPMOST, ext_styles & WS_EX_TOPMOST); #endif panel->Close(); } IN_PROC_BROWSER_TEST_F(PanelBrowserViewTest, PanelLayout) { // Create a fixed-size panel to avoid possible collapsing of the title // if the enforced min sizes are too small. Panel* panel = CreatePanelWithBounds("PanelTest", gfx::Rect(0, 0, 100, 50)); views::View* title_icon = GetTitleIcon(panel); views::View* title_text = GetTitleText(panel); views::View* close_button = GetCloseButton(panel); views::View* minimize_button = GetMinimizeButton(panel); views::View* restore_button = GetRestoreButton(panel); // We should have icon, text, minimize, restore and close buttons. Only one of // minimize and restore buttons are visible. EXPECT_EQ(5, GetControlCount(panel)); EXPECT_TRUE(ContainsControl(panel, title_icon)); EXPECT_TRUE(ContainsControl(panel, title_text)); EXPECT_TRUE(ContainsControl(panel, close_button)); EXPECT_TRUE(ContainsControl(panel, minimize_button)); EXPECT_TRUE(ContainsControl(panel, restore_button)); // These controls should be visible. EXPECT_TRUE(title_icon->visible()); EXPECT_TRUE(title_text->visible()); EXPECT_TRUE(close_button->visible()); EXPECT_TRUE(minimize_button->visible()); EXPECT_FALSE(restore_button->visible()); // Validate their layouts. int titlebar_height = GetTitlebarHeight(panel); EXPECT_GT(title_icon->width(), 0); EXPECT_GT(title_icon->height(), 0); EXPECT_LT(title_icon->height(), titlebar_height); EXPECT_GT(title_text->width(), 0); EXPECT_GT(title_text->height(), 0); EXPECT_LT(title_text->height(), titlebar_height); EXPECT_GT(minimize_button->width(), 0); EXPECT_GT(minimize_button->height(), 0); EXPECT_LT(minimize_button->height(), titlebar_height); EXPECT_GT(close_button->width(), 0); EXPECT_GT(close_button->height(), 0); EXPECT_LT(close_button->height(), titlebar_height); EXPECT_LT(title_icon->x() + title_icon->width(), title_text->x()); EXPECT_LT(title_text->x() + title_text->width(), minimize_button->x()); EXPECT_LT(minimize_button->x() + minimize_button->width(), close_button->x()); } IN_PROC_BROWSER_TEST_F(PanelBrowserViewTest, SetBoundsAnimation) { Panel* panel = CreatePanel("PanelTest"); PanelBrowserView* browser_view = GetBrowserView(panel); // The bounds animation should not be triggered when the panel is up for the // first time. EXPECT_FALSE(GetBoundsAnimator(panel)); // Validate that animation should be triggered when bounds are changed. gfx::Rect target_bounds(GetViewBounds(panel)); target_bounds.Offset(20, -30); target_bounds.set_width(target_bounds.width() + 100); target_bounds.set_height(target_bounds.height() + 50); SetViewBounds(panel, target_bounds); ASSERT_TRUE(GetBoundsAnimator(panel)); EXPECT_TRUE(GetBoundsAnimator(panel)->is_animating()); EXPECT_NE(GetViewBounds(panel), target_bounds); WaitTillBoundsAnimationFinished(panel); EXPECT_EQ(GetViewBounds(panel), target_bounds); // Validates that no animation should be triggered for the panel currently // being dragged. browser_view->OnTitlebarMousePressed(gfx::Point( target_bounds.x(), target_bounds.y())); browser_view->OnTitlebarMouseDragged(gfx::Point( target_bounds.x() + 5, target_bounds.y() + 5)); EXPECT_FALSE(GetBoundsAnimator(panel)->is_animating()); browser_view->OnTitlebarMouseCaptureLost(); panel->Close(); } IN_PROC_BROWSER_TEST_F(PanelBrowserViewTest, MinimizeAndRestoreOnNormalTaskBar) { TestMinimizeAndRestore(false); } IN_PROC_BROWSER_TEST_F(PanelBrowserViewTest, MinimizeAndRestoreOnAutoHideTaskBar) { TestMinimizeAndRestore(true); } IN_PROC_BROWSER_TEST_F(PanelBrowserViewTest, ChangeAutoHideTaskBarThickness) { TestChangeAutoHideTaskBarThickness(); } #endif