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 "chrome/browser/chrome_notification_types.h" 6 #include "chrome/browser/ui/browser.h" 7 #include "chrome/browser/ui/browser_finder.h" 8 #include "chrome/browser/ui/browser_list.h" 9 #include "chrome/browser/ui/browser_tabstrip.h" 10 #include "chrome/browser/ui/browser_window.h" 11 #include "chrome/browser/ui/views/frame/app_non_client_frame_view_ash.h" 12 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h" 13 #include "chrome/browser/ui/views/frame/browser_view.h" 14 #include "chrome/test/base/in_process_browser_test.h" 15 #include "content/public/test/test_utils.h" 16 #include "ui/aura/client/aura_constants.h" 17 #include "ui/aura/root_window.h" 18 #include "ui/aura/test/event_generator.h" 19 #include "ui/aura/window.h" 20 #include "ui/gfx/screen.h" 21 22 using aura::Window; 23 24 namespace { 25 26 Window* GetChildWindowNamed(Window* window, const char* name) { 27 for (size_t i = 0; i < window->children().size(); ++i) { 28 Window* child = window->children()[i]; 29 if (child->name() == name) 30 return child; 31 } 32 return NULL; 33 } 34 35 bool HasChildWindowNamed(Window* window, const char* name) { 36 return GetChildWindowNamed(window, name) != NULL; 37 } 38 39 void MaximizeWindow(aura::Window* window) { 40 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); 41 } 42 43 void MinimizeWindow(aura::Window* window) { 44 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED); 45 } 46 47 void RestoreWindow(Window* window) { 48 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); 49 } 50 51 } // namespace 52 53 class AppNonClientFrameViewAshTest : public InProcessBrowserTest { 54 public: 55 AppNonClientFrameViewAshTest() : InProcessBrowserTest(), app_browser_(NULL) { 56 } 57 virtual ~AppNonClientFrameViewAshTest() {} 58 59 virtual void SetUpOnMainThread() OVERRIDE { 60 Browser::CreateParams params = Browser::CreateParams::CreateForApp( 61 Browser::TYPE_POPUP, 62 std::string("Test"), 63 gfx::Rect(), 64 browser()->profile(), 65 browser()->host_desktop_type()); 66 params.initial_show_state = ui::SHOW_STATE_MAXIMIZED; 67 params.app_type = Browser::APP_TYPE_HOST; 68 app_browser_ = new Browser(params); 69 chrome::AddBlankTabAt(app_browser_, -1, true); 70 app_browser_->window()->Show(); 71 } 72 73 // Returns the class name of the NonClientFrameView. 74 std::string GetFrameClassName() const { 75 BrowserView* browser_view = 76 static_cast<BrowserView*>(app_browser_->window()); 77 BrowserFrame* browser_frame = browser_view->frame(); 78 return browser_frame->GetFrameView()->GetClassName(); 79 } 80 81 aura::RootWindow* GetRootWindow() const { 82 BrowserView* browser_view = 83 static_cast<BrowserView*>(app_browser_->window()); 84 views::Widget* widget = browser_view->GetWidget(); 85 aura::Window* window = 86 static_cast<aura::Window*>(widget->GetNativeWindow()); 87 return window->GetRootWindow(); 88 } 89 90 Browser* app_browser() const { return app_browser_; } 91 92 private: 93 Browser *app_browser_; 94 }; 95 96 #if defined(USE_ASH) 97 // Ensure that restoring the app window replaces the frame with a normal one, 98 // and maximizing again brings back the app frame. This has been the source of 99 // some crash bugs like crbug.com/155634 100 IN_PROC_BROWSER_TEST_F(AppNonClientFrameViewAshTest, SwitchFrames) { 101 // Convert to std::string so Windows can match EXPECT_EQ. 102 const std::string kAppFrameClassName = 103 AppNonClientFrameViewAsh::kViewClassName; 104 const std::string kNormalFrameClassName = 105 BrowserNonClientFrameViewAsh::kViewClassName; 106 107 // We start with the app frame. 108 EXPECT_EQ(kAppFrameClassName, GetFrameClassName()); 109 110 // Restoring the window gives us the normal frame. 111 Window* native_window = app_browser()->window()->GetNativeWindow(); 112 RestoreWindow(native_window); 113 EXPECT_EQ(kNormalFrameClassName, GetFrameClassName()); 114 115 // Maximizing the window switches back to the app frame. 116 MaximizeWindow(native_window); 117 EXPECT_EQ(kAppFrameClassName, GetFrameClassName()); 118 119 // Minimizing the window switches to normal frame. 120 // TODO(jamescook): This seems wasteful, since the user is likely to bring 121 // the window back to the maximized state. 122 MinimizeWindow(native_window); 123 EXPECT_EQ(kNormalFrameClassName, GetFrameClassName()); 124 125 // Coming back to maximized switches to app frame. 126 MaximizeWindow(native_window); 127 EXPECT_EQ(kAppFrameClassName, GetFrameClassName()); 128 129 // One more restore/maximize cycle for good measure. 130 RestoreWindow(native_window); 131 EXPECT_EQ(kNormalFrameClassName, GetFrameClassName()); 132 MaximizeWindow(native_window); 133 EXPECT_EQ(kAppFrameClassName, GetFrameClassName()); 134 } 135 #endif // USE_ASH 136 137 // Ensure that we can click the close button when the controls are shown. 138 // In particular make sure that we can click it on the top pixel of the button. 139 IN_PROC_BROWSER_TEST_F(AppNonClientFrameViewAshTest, ClickClose) { 140 aura::RootWindow* root_window = GetRootWindow(); 141 aura::test::EventGenerator eg(root_window, gfx::Point(0, 1)); 142 143 // Click close button. 144 eg.MoveMouseTo(root_window->bounds().width() - 1, 0); 145 content::WindowedNotificationObserver signal( 146 chrome::NOTIFICATION_BROWSER_CLOSED, 147 content::Source<Browser>(app_browser())); 148 eg.ClickLeftButton(); 149 signal.Wait(); 150 EXPECT_EQ(1u, chrome::GetBrowserCount(browser()->profile(), 151 browser()->host_desktop_type())); 152 } 153 154 // Ensure that closing a maximized app with Ctrl-W does not crash the 155 // application. crbug.com/147635 156 IN_PROC_BROWSER_TEST_F(AppNonClientFrameViewAshTest, KeyboardClose) { 157 aura::RootWindow* root_window = GetRootWindow(); 158 aura::test::EventGenerator eg(root_window); 159 160 // Base browser and app browser. 161 EXPECT_EQ(2u, chrome::GetBrowserCount(browser()->profile(), 162 browser()->host_desktop_type())); 163 164 // Send Control-W. 165 content::WindowedNotificationObserver signal( 166 chrome::NOTIFICATION_BROWSER_CLOSED, 167 content::Source<Browser>(app_browser())); 168 eg.PressKey(ui::VKEY_W, ui::EF_CONTROL_DOWN); 169 eg.ReleaseKey(ui::VKEY_W, ui::EF_CONTROL_DOWN); 170 signal.Wait(); 171 172 // App browser is closed. 173 EXPECT_EQ(1u, chrome::GetBrowserCount(browser()->profile(), 174 browser()->host_desktop_type())); 175 } 176 177 // Ensure that snapping left with Alt-[ closes the control window. 178 IN_PROC_BROWSER_TEST_F(AppNonClientFrameViewAshTest, SnapLeftClosesControls) { 179 aura::RootWindow* root_window = GetRootWindow(); 180 aura::test::EventGenerator eg(root_window); 181 aura::Window* native_window = app_browser()->window()->GetNativeWindow(); 182 183 // Control window exists. 184 EXPECT_TRUE(HasChildWindowNamed( 185 native_window, AppNonClientFrameViewAsh::kControlWindowName)); 186 187 // Send Alt-[ 188 eg.PressKey(ui::VKEY_OEM_4, ui::EF_ALT_DOWN); 189 eg.ReleaseKey(ui::VKEY_OEM_4, ui::EF_ALT_DOWN); 190 content::RunAllPendingInMessageLoop(); 191 192 // Control window is gone. 193 EXPECT_FALSE(HasChildWindowNamed( 194 native_window, AppNonClientFrameViewAsh::kControlWindowName)); 195 } 196 197 // Ensure that the controls are at the proper locations. 198 IN_PROC_BROWSER_TEST_F(AppNonClientFrameViewAshTest, ControlsAtRightSide) { 199 aura::RootWindow* root_window = GetRootWindow(); 200 aura::test::EventGenerator eg(root_window); 201 aura::Window* native_window = app_browser()->window()->GetNativeWindow(); 202 const gfx::Rect work_area = 203 gfx::Screen::GetScreenFor(native_window)->GetPrimaryDisplay().work_area(); 204 205 // Control window exists. 206 aura::Window* window = GetChildWindowNamed( 207 native_window, AppNonClientFrameViewAsh::kControlWindowName); 208 209 ASSERT_TRUE(window); 210 gfx::Rect rect = window->bounds(); 211 EXPECT_EQ(work_area.right(), rect.right()); 212 EXPECT_EQ(work_area.y(), rect.y()); 213 214 MinimizeWindow(native_window); 215 content::RunAllPendingInMessageLoop(); 216 window = GetChildWindowNamed( 217 native_window, AppNonClientFrameViewAsh::kControlWindowName); 218 EXPECT_FALSE(window); 219 MaximizeWindow(native_window); 220 content::RunAllPendingInMessageLoop(); 221 222 // Control window exists. 223 aura::Window* window_after = GetChildWindowNamed( 224 native_window, AppNonClientFrameViewAsh::kControlWindowName); 225 ASSERT_TRUE(window_after); 226 gfx::Rect rect_after = window_after->bounds(); 227 EXPECT_EQ(work_area.right(), rect_after.right()); 228 EXPECT_EQ(work_area.y(), rect_after.y()); 229 } 230