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. 4 5 #include "chrome/browser/apps/app_browsertest_util.h" 6 #include "chrome/test/base/interactive_test_utils.h" 7 #include "extensions/browser/app_window/native_app_window.h" 8 #include "extensions/test/extension_test_message_listener.h" 9 #include "extensions/test/result_catcher.h" 10 11 #if defined(OS_MACOSX) && !defined(OS_IOS) 12 #include "base/mac/mac_util.h" 13 #endif 14 15 #if defined(OS_WIN) 16 #include <windows.h> 17 #include "ui/aura/window.h" 18 #include "ui/aura/window_tree_host.h" 19 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h" 20 #include "ui/views/win/hwnd_message_handler_delegate.h" 21 #include "ui/views/win/hwnd_util.h" 22 #endif 23 24 using extensions::AppWindow; 25 using extensions::NativeAppWindow; 26 27 // Helper class that has to be created in the stack to check if the fullscreen 28 // setting of a NativeWindow has changed since the creation of the object. 29 class FullscreenChangeWaiter { 30 public: 31 explicit FullscreenChangeWaiter(NativeAppWindow* window) 32 : window_(window), 33 initial_fullscreen_state_(window_->IsFullscreen()) {} 34 35 void Wait() { 36 while (initial_fullscreen_state_ == window_->IsFullscreen()) 37 content::RunAllPendingInMessageLoop(); 38 } 39 40 private: 41 NativeAppWindow* window_; 42 bool initial_fullscreen_state_; 43 44 DISALLOW_COPY_AND_ASSIGN(FullscreenChangeWaiter); 45 }; 46 47 class AppWindowInteractiveTest : public extensions::PlatformAppBrowserTest { 48 public: 49 bool RunAppWindowInteractiveTest(const char* testName) { 50 ExtensionTestMessageListener launched_listener("Launched", true); 51 LoadAndLaunchPlatformApp("window_api_interactive", &launched_listener); 52 53 extensions::ResultCatcher catcher; 54 launched_listener.Reply(testName); 55 56 if (!catcher.GetNextResult()) { 57 message_ = catcher.message(); 58 return false; 59 } 60 61 return true; 62 } 63 64 bool SimulateKeyPress(ui::KeyboardCode key) { 65 return ui_test_utils::SendKeyPressToWindowSync( 66 GetFirstAppWindow()->GetNativeWindow(), 67 key, 68 false, 69 false, 70 false, 71 false); 72 } 73 74 // This method will wait until the application is able to ack a key event. 75 void WaitUntilKeyFocus() { 76 ExtensionTestMessageListener key_listener("KeyReceived", false); 77 78 while (!key_listener.was_satisfied()) { 79 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_Z)); 80 content::RunAllPendingInMessageLoop(); 81 } 82 } 83 84 // This test is a method so that we can test with each frame type. 85 void TestOuterBoundsHelper(const std::string& frame_type); 86 }; 87 88 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, ESCLeavesFullscreenWindow) { 89 // This test is flaky on MacOS 10.6. 90 #if defined(OS_MACOSX) && !defined(OS_IOS) 91 if (base::mac::IsOSSnowLeopard()) 92 return; 93 #endif 94 95 ExtensionTestMessageListener launched_listener("Launched", true); 96 LoadAndLaunchPlatformApp("leave_fullscreen", &launched_listener); 97 98 // We start by making sure the window is actually focused. 99 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow( 100 GetFirstAppWindow()->GetNativeWindow())); 101 102 // When receiving the reply, the application will try to go fullscreen using 103 // the Window API but there is no synchronous way to know if that actually 104 // succeeded. Also, failure will not be notified. A failure case will only be 105 // known with a timeout. 106 { 107 FullscreenChangeWaiter fs_changed(GetFirstAppWindow()->GetBaseWindow()); 108 109 launched_listener.Reply("window"); 110 111 fs_changed.Wait(); 112 } 113 114 // Depending on the platform, going fullscreen might create an animation. 115 // We want to make sure that the ESC key we will send next is actually going 116 // to be received and the application might not receive key events during the 117 // animation so we should wait for the key focus to be back. 118 WaitUntilKeyFocus(); 119 120 // Same idea as above but for leaving fullscreen. Fullscreen mode should be 121 // left when ESC is received. 122 { 123 FullscreenChangeWaiter fs_changed(GetFirstAppWindow()->GetBaseWindow()); 124 125 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE)); 126 127 fs_changed.Wait(); 128 } 129 } 130 131 #if defined(OS_MACOSX) 132 // http://crbug.com/406009 133 #define MAYBE_ESCLeavesFullscreenDOM DISABLED_ESCLeavesFullscreenDOM 134 #else 135 #define MAYBE_ESCLeavesFullscreenDOM ESCLeavesFullscreenDOM 136 #endif 137 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, MAYBE_ESCLeavesFullscreenDOM) { 138 // This test is flaky on MacOS 10.6. 139 #if defined(OS_MACOSX) && !defined(OS_IOS) 140 if (base::mac::IsOSSnowLeopard()) 141 return; 142 #endif 143 144 ExtensionTestMessageListener launched_listener("Launched", true); 145 LoadAndLaunchPlatformApp("leave_fullscreen", &launched_listener); 146 147 // We start by making sure the window is actually focused. 148 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow( 149 GetFirstAppWindow()->GetNativeWindow())); 150 151 launched_listener.Reply("dom"); 152 153 // Because the DOM way to go fullscreen requires user gesture, we simulate a 154 // key event to get the window entering in fullscreen mode. The reply will 155 // make the window listen for the key event. The reply will be sent to the 156 // renderer process before the keypress and should be received in that order. 157 // When receiving the key event, the application will try to go fullscreen 158 // using the Window API but there is no synchronous way to know if that 159 // actually succeeded. Also, failure will not be notified. A failure case will 160 // only be known with a timeout. 161 { 162 FullscreenChangeWaiter fs_changed(GetFirstAppWindow()->GetBaseWindow()); 163 164 WaitUntilKeyFocus(); 165 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_A)); 166 167 fs_changed.Wait(); 168 } 169 170 // Depending on the platform, going fullscreen might create an animation. 171 // We want to make sure that the ESC key we will send next is actually going 172 // to be received and the application might not receive key events during the 173 // animation so we should wait for the key focus to be back. 174 WaitUntilKeyFocus(); 175 176 // Same idea as above but for leaving fullscreen. Fullscreen mode should be 177 // left when ESC is received. 178 { 179 FullscreenChangeWaiter fs_changed(GetFirstAppWindow()->GetBaseWindow()); 180 181 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE)); 182 183 fs_changed.Wait(); 184 } 185 } 186 187 #if defined(OS_MACOSX) 188 // http://crbug.com/406009 189 #define MAYBE_ESCDoesNotLeaveFullscreenWindow DISABLED_ESCDoesNotLeaveFullscreenWindow 190 #else 191 #define MAYBE_ESCDoesNotLeaveFullscreenWindow ESCDoesNotLeaveFullscreenWindow 192 #endif 193 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, 194 MAYBE_ESCDoesNotLeaveFullscreenWindow) { 195 // This test is flaky on MacOS 10.6. 196 #if defined(OS_MACOSX) && !defined(OS_IOS) 197 if (base::mac::IsOSSnowLeopard()) 198 return; 199 #endif 200 201 ExtensionTestMessageListener launched_listener("Launched", true); 202 LoadAndLaunchPlatformApp("prevent_leave_fullscreen", &launched_listener); 203 204 // We start by making sure the window is actually focused. 205 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow( 206 GetFirstAppWindow()->GetNativeWindow())); 207 208 // When receiving the reply, the application will try to go fullscreen using 209 // the Window API but there is no synchronous way to know if that actually 210 // succeeded. Also, failure will not be notified. A failure case will only be 211 // known with a timeout. 212 { 213 FullscreenChangeWaiter fs_changed(GetFirstAppWindow()->GetBaseWindow()); 214 215 launched_listener.Reply("window"); 216 217 fs_changed.Wait(); 218 } 219 220 // Depending on the platform, going fullscreen might create an animation. 221 // We want to make sure that the ESC key we will send next is actually going 222 // to be received and the application might not receive key events during the 223 // animation so we should wait for the key focus to be back. 224 WaitUntilKeyFocus(); 225 226 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE)); 227 228 ExtensionTestMessageListener second_key_listener("B_KEY_RECEIVED", false); 229 230 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_B)); 231 232 ASSERT_TRUE(second_key_listener.WaitUntilSatisfied()); 233 234 // We assume that at that point, if we had to leave fullscreen, we should be. 235 // However, by nature, we can not guarantee that and given that we do test 236 // that nothing happens, we might end up with random-success when the feature 237 // is broken. 238 EXPECT_TRUE(GetFirstAppWindow()->GetBaseWindow()->IsFullscreen()); 239 } 240 241 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, 242 ESCDoesNotLeaveFullscreenDOM) { 243 // This test is flaky on MacOS 10.6. 244 #if defined(OS_MACOSX) && !defined(OS_IOS) 245 if (base::mac::IsOSSnowLeopard()) 246 return; 247 #endif 248 249 ExtensionTestMessageListener launched_listener("Launched", true); 250 LoadAndLaunchPlatformApp("prevent_leave_fullscreen", &launched_listener); 251 252 // We start by making sure the window is actually focused. 253 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow( 254 GetFirstAppWindow()->GetNativeWindow())); 255 256 launched_listener.Reply("dom"); 257 258 // Because the DOM way to go fullscreen requires user gesture, we simulate a 259 // key event to get the window entering in fullscreen mode. The reply will 260 // make the window listen for the key event. The reply will be sent to the 261 // renderer process before the keypress and should be received in that order. 262 // When receiving the key event, the application will try to go fullscreen 263 // using the Window API but there is no synchronous way to know if that 264 // actually succeeded. Also, failure will not be notified. A failure case will 265 // only be known with a timeout. 266 { 267 FullscreenChangeWaiter fs_changed(GetFirstAppWindow()->GetBaseWindow()); 268 269 WaitUntilKeyFocus(); 270 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_A)); 271 272 fs_changed.Wait(); 273 } 274 275 // Depending on the platform, going fullscreen might create an animation. 276 // We want to make sure that the ESC key we will send next is actually going 277 // to be received and the application might not receive key events during the 278 // animation so we should wait for the key focus to be back. 279 WaitUntilKeyFocus(); 280 281 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE)); 282 283 ExtensionTestMessageListener second_key_listener("B_KEY_RECEIVED", false); 284 285 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_B)); 286 287 ASSERT_TRUE(second_key_listener.WaitUntilSatisfied()); 288 289 // We assume that at that point, if we had to leave fullscreen, we should be. 290 // However, by nature, we can not guarantee that and given that we do test 291 // that nothing happens, we might end up with random-success when the feature 292 // is broken. 293 EXPECT_TRUE(GetFirstAppWindow()->GetBaseWindow()->IsFullscreen()); 294 } 295 296 // This test is duplicated from ESCDoesNotLeaveFullscreenWindow. 297 // It runs the same test, but uses the old permission names: 'fullscreen' 298 // and 'overrideEscFullscreen'. 299 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, 300 ESCDoesNotLeaveFullscreenOldPermission) { 301 // This test is flaky on MacOS 10.6. 302 #if defined(OS_MACOSX) && !defined(OS_IOS) 303 if (base::mac::IsOSSnowLeopard()) 304 return; 305 #endif 306 307 ExtensionTestMessageListener launched_listener("Launched", true); 308 LoadAndLaunchPlatformApp("prevent_leave_fullscreen_old", &launched_listener); 309 310 // We start by making sure the window is actually focused. 311 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow( 312 GetFirstAppWindow()->GetNativeWindow())); 313 314 // When receiving the reply, the application will try to go fullscreen using 315 // the Window API but there is no synchronous way to know if that actually 316 // succeeded. Also, failure will not be notified. A failure case will only be 317 // known with a timeout. 318 { 319 FullscreenChangeWaiter fs_changed(GetFirstAppWindow()->GetBaseWindow()); 320 321 launched_listener.Reply("window"); 322 323 fs_changed.Wait(); 324 } 325 326 // Depending on the platform, going fullscreen might create an animation. 327 // We want to make sure that the ESC key we will send next is actually going 328 // to be received and the application might not receive key events during the 329 // animation so we should wait for the key focus to be back. 330 WaitUntilKeyFocus(); 331 332 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE)); 333 334 ExtensionTestMessageListener second_key_listener("B_KEY_RECEIVED", false); 335 336 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_B)); 337 338 ASSERT_TRUE(second_key_listener.WaitUntilSatisfied()); 339 340 // We assume that at that point, if we had to leave fullscreen, we should be. 341 // However, by nature, we can not guarantee that and given that we do test 342 // that nothing happens, we might end up with random-success when the feature 343 // is broken. 344 EXPECT_TRUE(GetFirstAppWindow()->GetBaseWindow()->IsFullscreen()); 345 } 346 347 #if defined(OS_MACOSX) || defined(OS_WIN) 348 // http://crbug.com/404081 349 #define MAYBE_TestInnerBounds DISABLED_TestInnerBounds 350 #else 351 #define MAYBE_TestInnerBounds TestInnerBounds 352 #endif 353 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, MAYBE_TestInnerBounds) { 354 ASSERT_TRUE(RunAppWindowInteractiveTest("testInnerBounds")) << message_; 355 } 356 357 void AppWindowInteractiveTest::TestOuterBoundsHelper( 358 const std::string& frame_type) { 359 ExtensionTestMessageListener launched_listener("Launched", true); 360 const extensions::Extension* app = 361 LoadAndLaunchPlatformApp("outer_bounds", &launched_listener); 362 363 launched_listener.Reply(frame_type); 364 launched_listener.Reset(); 365 ASSERT_TRUE(launched_listener.WaitUntilSatisfied()); 366 367 AppWindow* window = GetFirstAppWindowForApp(app->id()); 368 gfx::Rect window_bounds; 369 gfx::Size min_size, max_size; 370 371 #if defined(OS_WIN) 372 // Get the bounds from the HWND. 373 HWND hwnd = views::HWNDForNativeWindow(window->GetNativeWindow()); 374 RECT rect; 375 ::GetWindowRect(hwnd, &rect); 376 window_bounds = gfx::Rect( 377 rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); 378 379 // HWNDMessageHandler calls this when responding to WM_GETMINMAXSIZE, so it's 380 // the closest to what the window will see. 381 views::HWNDMessageHandlerDelegate* host = 382 static_cast<views::HWNDMessageHandlerDelegate*>( 383 static_cast<views::DesktopWindowTreeHostWin*>( 384 aura::WindowTreeHost::GetForAcceleratedWidget(hwnd))); 385 host->GetMinMaxSize(&min_size, &max_size); 386 // Note that this does not include the the client area insets so we need to 387 // add them. 388 gfx::Insets insets; 389 host->GetClientAreaInsets(&insets); 390 min_size = gfx::Size(min_size.width() + insets.left() + insets.right(), 391 min_size.height() + insets.top() + insets.bottom()); 392 max_size = gfx::Size( 393 max_size.width() ? max_size.width() + insets.left() + insets.right() : 0, 394 max_size.height() ? max_size.height() + insets.top() + insets.bottom() 395 : 0); 396 #endif // defined(OS_WIN) 397 398 // These match the values in the outer_bounds/test.js 399 EXPECT_EQ(gfx::Rect(10, 11, 300, 301), window_bounds); 400 EXPECT_EQ(window->GetBaseWindow()->GetBounds(), window_bounds); 401 EXPECT_EQ(200, min_size.width()); 402 EXPECT_EQ(201, min_size.height()); 403 EXPECT_EQ(400, max_size.width()); 404 EXPECT_EQ(401, max_size.height()); 405 } 406 407 // TODO(jackhou): Make this test work for other OSes. 408 #if !defined(OS_WIN) 409 #define MAYBE_TestOuterBoundsFrameChrome DISABLED_TestOuterBoundsFrameChrome 410 #define MAYBE_TestOuterBoundsFrameNone DISABLED_TestOuterBoundsFrameNone 411 #define MAYBE_TestOuterBoundsFrameColor DISABLED_TestOuterBoundsFrameColor 412 #else 413 #define MAYBE_TestOuterBoundsFrameChrome TestOuterBoundsFrameChrome 414 #define MAYBE_TestOuterBoundsFrameNone TestOuterBoundsFrameNone 415 #define MAYBE_TestOuterBoundsFrameColor TestOuterBoundsFrameColor 416 #endif 417 418 // Test that the outer bounds match that of the native window. 419 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, 420 MAYBE_TestOuterBoundsFrameChrome) { 421 TestOuterBoundsHelper("chrome"); 422 } 423 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, 424 MAYBE_TestOuterBoundsFrameNone) { 425 TestOuterBoundsHelper("none"); 426 } 427 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, 428 MAYBE_TestOuterBoundsFrameColor) { 429 TestOuterBoundsHelper("color"); 430 } 431 432 // This test does not work on Linux Aura because ShowInactive() is not 433 // implemented. See http://crbug.com/325142 434 // It also does not work on Windows because of the document being focused even 435 // though the window is not activated. See http://crbug.com/326986 436 // It also does not work on MacOS because ::ShowInactive() ends up behaving like 437 // ::Show() because of Cocoa conventions. See http://crbug.com/326987 438 // Those tests should be disabled on Linux GTK when they are enabled on the 439 // other platforms, see http://crbug.com/328829 440 #if (defined(OS_LINUX) && defined(USE_AURA)) || \ 441 defined(OS_WIN) || defined(OS_MACOSX) 442 #define MAYBE_TestCreate DISABLED_TestCreate 443 #define MAYBE_TestShow DISABLED_TestShow 444 #else 445 #define MAYBE_TestCreate TestCreate 446 #define MAYBE_TestShow TestShow 447 #endif 448 449 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, MAYBE_TestCreate) { 450 ASSERT_TRUE(RunAppWindowInteractiveTest("testCreate")) << message_; 451 } 452 453 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, MAYBE_TestShow) { 454 ASSERT_TRUE(RunAppWindowInteractiveTest("testShow")) << message_; 455 } 456 457 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest, TestDrawAttention) { 458 ASSERT_TRUE(RunAppWindowInteractiveTest("testDrawAttention")) << message_; 459 } 460