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 "chrome/browser/apps/app_browsertest_util.h"
6 #include "chrome/browser/lifetime/application_lifetime.h"
7 #include "chrome/browser/ui/browser_iterator.h"
8 #include "chrome/browser/ui/browser_window.h"
9 #include "chrome/test/base/interactive_test_utils.h"
10 #include "extensions/browser/app_window/native_app_window.h"
11 #include "extensions/test/extension_test_message_listener.h"
12 #include "extensions/test/result_catcher.h"
14 #if defined(OS_MACOSX)
15 #include "base/mac/mac_util.h"
16 #include "ui/base/test/scoped_fake_nswindow_fullscreen.h"
21 #include "ui/aura/window.h"
22 #include "ui/aura/window_tree_host.h"
23 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
24 #include "ui/views/win/hwnd_message_handler_delegate.h"
25 #include "ui/views/win/hwnd_util.h"
28 using extensions::AppWindow
;
29 using extensions::NativeAppWindow
;
31 // Helper class that has to be created in the stack to check if the fullscreen
32 // setting of a NativeWindow has changed since the creation of the object.
33 class FullscreenChangeWaiter
{
35 explicit FullscreenChangeWaiter(NativeAppWindow
* window
)
37 initial_fullscreen_state_(window_
->IsFullscreen()) {}
40 while (initial_fullscreen_state_
== window_
->IsFullscreen())
41 content::RunAllPendingInMessageLoop();
45 NativeAppWindow
* window_
;
46 bool initial_fullscreen_state_
;
48 DISALLOW_COPY_AND_ASSIGN(FullscreenChangeWaiter
);
51 class AppWindowInteractiveTest
: public extensions::PlatformAppBrowserTest
{
53 bool RunAppWindowInteractiveTest(const char* testName
) {
54 ExtensionTestMessageListener
launched_listener("Launched", true);
55 LoadAndLaunchPlatformApp("window_api_interactive", &launched_listener
);
57 extensions::ResultCatcher catcher
;
58 launched_listener
.Reply(testName
);
60 if (!catcher
.GetNextResult()) {
61 message_
= catcher
.message();
68 bool SimulateKeyPress(ui::KeyboardCode key
) {
69 return ui_test_utils::SendKeyPressToWindowSync(
70 GetFirstAppWindow()->GetNativeWindow(),
78 // This method will wait until the application is able to ack a key event.
79 void WaitUntilKeyFocus() {
80 ExtensionTestMessageListener
key_listener("KeyReceived", false);
82 while (!key_listener
.was_satisfied()) {
83 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_Z
));
84 content::RunAllPendingInMessageLoop();
88 // This test is a method so that we can test with each frame type.
89 void TestOuterBoundsHelper(const std::string
& frame_type
);
92 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, ESCLeavesFullscreenWindow
) {
93 // This test is flaky on MacOS 10.6.
94 #if defined(OS_MACOSX)
95 if (base::mac::IsOSSnowLeopard())
98 ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen
;
101 ExtensionTestMessageListener
launched_listener("Launched", true);
102 LoadAndLaunchPlatformApp("leave_fullscreen", &launched_listener
);
104 // We start by making sure the window is actually focused.
105 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
106 GetFirstAppWindow()->GetNativeWindow()));
108 // When receiving the reply, the application will try to go fullscreen using
109 // the Window API but there is no synchronous way to know if that actually
110 // succeeded. Also, failure will not be notified. A failure case will only be
111 // known with a timeout.
113 FullscreenChangeWaiter
fs_changed(GetFirstAppWindow()->GetBaseWindow());
115 launched_listener
.Reply("window");
120 // Depending on the platform, going fullscreen might create an animation.
121 // We want to make sure that the ESC key we will send next is actually going
122 // to be received and the application might not receive key events during the
123 // animation so we should wait for the key focus to be back.
126 // Same idea as above but for leaving fullscreen. Fullscreen mode should be
127 // left when ESC is received.
129 FullscreenChangeWaiter
fs_changed(GetFirstAppWindow()->GetBaseWindow());
131 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE
));
137 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, ESCLeavesFullscreenDOM
) {
138 // This test is flaky on MacOS 10.6.
139 #if defined(OS_MACOSX)
140 if (base::mac::IsOSSnowLeopard())
143 ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen
;
146 ExtensionTestMessageListener
launched_listener("Launched", true);
147 LoadAndLaunchPlatformApp("leave_fullscreen", &launched_listener
);
149 // We start by making sure the window is actually focused.
150 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
151 GetFirstAppWindow()->GetNativeWindow()));
153 launched_listener
.Reply("dom");
155 // Because the DOM way to go fullscreen requires user gesture, we simulate a
156 // key event to get the window entering in fullscreen mode. The reply will
157 // make the window listen for the key event. The reply will be sent to the
158 // renderer process before the keypress and should be received in that order.
159 // When receiving the key event, the application will try to go fullscreen
160 // using the Window API but there is no synchronous way to know if that
161 // actually succeeded. Also, failure will not be notified. A failure case will
162 // only be known with a timeout.
164 FullscreenChangeWaiter
fs_changed(GetFirstAppWindow()->GetBaseWindow());
167 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_A
));
172 // Depending on the platform, going fullscreen might create an animation.
173 // We want to make sure that the ESC key we will send next is actually going
174 // to be received and the application might not receive key events during the
175 // animation so we should wait for the key focus to be back.
178 // Same idea as above but for leaving fullscreen. Fullscreen mode should be
179 // left when ESC is received.
181 FullscreenChangeWaiter
fs_changed(GetFirstAppWindow()->GetBaseWindow());
183 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE
));
189 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
,
190 ESCDoesNotLeaveFullscreenWindow
) {
191 // This test is flaky on MacOS 10.6.
192 #if defined(OS_MACOSX)
193 if (base::mac::IsOSSnowLeopard())
196 ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen
;
199 ExtensionTestMessageListener
launched_listener("Launched", true);
200 LoadAndLaunchPlatformApp("prevent_leave_fullscreen", &launched_listener
);
202 // We start by making sure the window is actually focused.
203 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
204 GetFirstAppWindow()->GetNativeWindow()));
206 // When receiving the reply, the application will try to go fullscreen using
207 // the Window API but there is no synchronous way to know if that actually
208 // succeeded. Also, failure will not be notified. A failure case will only be
209 // known with a timeout.
211 FullscreenChangeWaiter
fs_changed(GetFirstAppWindow()->GetBaseWindow());
213 launched_listener
.Reply("window");
218 // Depending on the platform, going fullscreen might create an animation.
219 // We want to make sure that the ESC key we will send next is actually going
220 // to be received and the application might not receive key events during the
221 // animation so we should wait for the key focus to be back.
224 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE
));
226 ExtensionTestMessageListener
second_key_listener("B_KEY_RECEIVED", false);
228 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_B
));
230 ASSERT_TRUE(second_key_listener
.WaitUntilSatisfied());
232 // We assume that at that point, if we had to leave fullscreen, we should be.
233 // However, by nature, we can not guarantee that and given that we do test
234 // that nothing happens, we might end up with random-success when the feature
236 EXPECT_TRUE(GetFirstAppWindow()->GetBaseWindow()->IsFullscreen());
239 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
,
240 ESCDoesNotLeaveFullscreenDOM
) {
241 // This test is flaky on MacOS 10.6.
242 #if defined(OS_MACOSX)
243 if (base::mac::IsOSSnowLeopard())
246 ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen
;
249 ExtensionTestMessageListener
launched_listener("Launched", true);
250 LoadAndLaunchPlatformApp("prevent_leave_fullscreen", &launched_listener
);
252 // We start by making sure the window is actually focused.
253 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
254 GetFirstAppWindow()->GetNativeWindow()));
256 launched_listener
.Reply("dom");
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.
267 FullscreenChangeWaiter
fs_changed(GetFirstAppWindow()->GetBaseWindow());
270 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_A
));
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.
281 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE
));
283 ExtensionTestMessageListener
second_key_listener("B_KEY_RECEIVED", false);
285 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_B
));
287 ASSERT_TRUE(second_key_listener
.WaitUntilSatisfied());
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
293 EXPECT_TRUE(GetFirstAppWindow()->GetBaseWindow()->IsFullscreen());
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)
303 if (base::mac::IsOSSnowLeopard())
306 ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen
;
309 ExtensionTestMessageListener
launched_listener("Launched", true);
310 LoadAndLaunchPlatformApp("prevent_leave_fullscreen_old", &launched_listener
);
312 // We start by making sure the window is actually focused.
313 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
314 GetFirstAppWindow()->GetNativeWindow()));
316 // When receiving the reply, the application will try to go fullscreen using
317 // the Window API but there is no synchronous way to know if that actually
318 // succeeded. Also, failure will not be notified. A failure case will only be
319 // known with a timeout.
321 FullscreenChangeWaiter
fs_changed(GetFirstAppWindow()->GetBaseWindow());
323 launched_listener
.Reply("window");
328 // Depending on the platform, going fullscreen might create an animation.
329 // We want to make sure that the ESC key we will send next is actually going
330 // to be received and the application might not receive key events during the
331 // animation so we should wait for the key focus to be back.
334 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE
));
336 ExtensionTestMessageListener
second_key_listener("B_KEY_RECEIVED", false);
338 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_B
));
340 ASSERT_TRUE(second_key_listener
.WaitUntilSatisfied());
342 // We assume that at that point, if we had to leave fullscreen, we should be.
343 // However, by nature, we can not guarantee that and given that we do test
344 // that nothing happens, we might end up with random-success when the feature
346 EXPECT_TRUE(GetFirstAppWindow()->GetBaseWindow()->IsFullscreen());
349 #if defined(OS_MACOSX) || defined(OS_WIN)
350 // http://crbug.com/404081
351 #define MAYBE_TestInnerBounds DISABLED_TestInnerBounds
353 #define MAYBE_TestInnerBounds TestInnerBounds
355 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, MAYBE_TestInnerBounds
) {
356 ASSERT_TRUE(RunAppWindowInteractiveTest("testInnerBounds")) << message_
;
359 void AppWindowInteractiveTest::TestOuterBoundsHelper(
360 const std::string
& frame_type
) {
361 ExtensionTestMessageListener
launched_listener("Launched", true);
362 const extensions::Extension
* app
=
363 LoadAndLaunchPlatformApp("outer_bounds", &launched_listener
);
365 launched_listener
.Reply(frame_type
);
366 launched_listener
.Reset();
367 ASSERT_TRUE(launched_listener
.WaitUntilSatisfied());
369 AppWindow
* window
= GetFirstAppWindowForApp(app
->id());
370 gfx::Rect window_bounds
;
371 gfx::Size min_size
, max_size
;
374 // Get the bounds from the HWND.
375 HWND hwnd
= views::HWNDForNativeWindow(window
->GetNativeWindow());
377 ::GetWindowRect(hwnd
, &rect
);
378 window_bounds
= gfx::Rect(
379 rect
.left
, rect
.top
, rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
381 // HWNDMessageHandler calls this when responding to WM_GETMINMAXSIZE, so it's
382 // the closest to what the window will see.
383 views::HWNDMessageHandlerDelegate
* host
=
384 static_cast<views::HWNDMessageHandlerDelegate
*>(
385 static_cast<views::DesktopWindowTreeHostWin
*>(
386 aura::WindowTreeHost::GetForAcceleratedWidget(hwnd
)));
387 host
->GetMinMaxSize(&min_size
, &max_size
);
388 // Note that this does not include the the client area insets so we need to
391 host
->GetClientAreaInsets(&insets
);
392 min_size
= gfx::Size(min_size
.width() + insets
.left() + insets
.right(),
393 min_size
.height() + insets
.top() + insets
.bottom());
394 max_size
= gfx::Size(
395 max_size
.width() ? max_size
.width() + insets
.left() + insets
.right() : 0,
396 max_size
.height() ? max_size
.height() + insets
.top() + insets
.bottom()
398 #endif // defined(OS_WIN)
400 // These match the values in the outer_bounds/test.js
401 EXPECT_EQ(gfx::Rect(10, 11, 300, 301), window_bounds
);
402 EXPECT_EQ(window
->GetBaseWindow()->GetBounds(), window_bounds
);
403 EXPECT_EQ(200, min_size
.width());
404 EXPECT_EQ(201, min_size
.height());
405 EXPECT_EQ(400, max_size
.width());
406 EXPECT_EQ(401, max_size
.height());
409 // TODO(jackhou): Make this test work for other OSes.
411 #define MAYBE_TestOuterBoundsFrameChrome DISABLED_TestOuterBoundsFrameChrome
412 #define MAYBE_TestOuterBoundsFrameNone DISABLED_TestOuterBoundsFrameNone
413 #define MAYBE_TestOuterBoundsFrameColor DISABLED_TestOuterBoundsFrameColor
415 #define MAYBE_TestOuterBoundsFrameChrome TestOuterBoundsFrameChrome
416 #define MAYBE_TestOuterBoundsFrameNone TestOuterBoundsFrameNone
417 #define MAYBE_TestOuterBoundsFrameColor TestOuterBoundsFrameColor
420 // Test that the outer bounds match that of the native window.
421 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
,
422 MAYBE_TestOuterBoundsFrameChrome
) {
423 TestOuterBoundsHelper("chrome");
425 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
,
426 MAYBE_TestOuterBoundsFrameNone
) {
427 TestOuterBoundsHelper("none");
429 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
,
430 MAYBE_TestOuterBoundsFrameColor
) {
431 TestOuterBoundsHelper("color");
434 // This test does not work on Linux Aura because ShowInactive() is not
435 // implemented. See http://crbug.com/325142
436 // It also does not work on Windows because of the document being focused even
437 // though the window is not activated. See http://crbug.com/326986
438 // It also does not work on MacOS because ::ShowInactive() ends up behaving like
439 // ::Show() because of Cocoa conventions. See http://crbug.com/326987
440 // Those tests should be disabled on Linux GTK when they are enabled on the
441 // other platforms, see http://crbug.com/328829
442 #if (defined(OS_LINUX) && defined(USE_AURA)) || \
443 defined(OS_WIN) || defined(OS_MACOSX)
444 #define MAYBE_TestCreate DISABLED_TestCreate
445 #define MAYBE_TestShow DISABLED_TestShow
447 #define MAYBE_TestCreate TestCreate
448 #define MAYBE_TestShow TestShow
451 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, MAYBE_TestCreate
) {
452 ASSERT_TRUE(RunAppWindowInteractiveTest("testCreate")) << message_
;
455 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, MAYBE_TestShow
) {
456 ASSERT_TRUE(RunAppWindowInteractiveTest("testShow")) << message_
;
459 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, TestDrawAttention
) {
460 ASSERT_TRUE(RunAppWindowInteractiveTest("testDrawAttention")) << message_
;
463 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, TestCreateHidden
) {
464 // Created hidden both times.
466 ExtensionTestMessageListener
launched_listener("Launched", true);
467 LoadAndLaunchPlatformApp("hidden_with_id", &launched_listener
);
468 EXPECT_TRUE(launched_listener
.WaitUntilSatisfied());
469 ExtensionTestMessageListener
create_listener_1("Launched", true);
470 launched_listener
.Reply("createHidden");
471 EXPECT_TRUE(create_listener_1
.WaitUntilSatisfied());
472 AppWindow
* app_window
= GetFirstAppWindow();
473 EXPECT_TRUE(app_window
->is_hidden());
474 ExtensionTestMessageListener
create_listener_2("Launched", false);
475 create_listener_1
.Reply("createHidden");
476 EXPECT_TRUE(create_listener_2
.WaitUntilSatisfied());
477 EXPECT_TRUE(app_window
->is_hidden());
478 app_window
->GetBaseWindow()->Close();
481 // Created hidden, then visible. The second create should show the window.
483 ExtensionTestMessageListener
launched_listener("Launched", true);
484 LoadAndLaunchPlatformApp("hidden_with_id", &launched_listener
);
485 EXPECT_TRUE(launched_listener
.WaitUntilSatisfied());
486 ExtensionTestMessageListener
create_listener_1("Launched", true);
487 launched_listener
.Reply("createHidden");
488 EXPECT_TRUE(create_listener_1
.WaitUntilSatisfied());
489 AppWindow
* app_window
= GetFirstAppWindow();
490 EXPECT_TRUE(app_window
->is_hidden());
491 ExtensionTestMessageListener
create_listener_2("Launched", false);
492 create_listener_1
.Reply("createVisible");
493 EXPECT_TRUE(create_listener_2
.WaitUntilSatisfied());
494 EXPECT_FALSE(app_window
->is_hidden());
495 app_window
->GetBaseWindow()->Close();
499 #if defined(OS_MACOSX)
500 // http://crbug.com/502516
501 #define MAYBE_TestFullscreen DISABLED_TestFullscreen
503 #define MAYBE_TestFullscreen TestFullscreen
505 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, MAYBE_TestFullscreen
) {
506 ASSERT_TRUE(RunAppWindowInteractiveTest("testFullscreen")) << message_
;
509 // Only Linux and Windows use keep-alive to determine when to shut down.
510 #if defined(OS_LINUX) || defined(OS_WIN)
512 // In general, hidden windows should not keep Chrome alive. The exception is
513 // when windows are created hidden, we allow the app some time to show the
515 class AppWindowHiddenKeepAliveTest
: public extensions::PlatformAppBrowserTest
{
517 AppWindowHiddenKeepAliveTest() {}
520 DISALLOW_COPY_AND_ASSIGN(AppWindowHiddenKeepAliveTest
);
523 // A window that becomes hidden should not keep Chrome alive.
524 IN_PROC_BROWSER_TEST_F(AppWindowHiddenKeepAliveTest
, ShownThenHidden
) {
525 LoadAndLaunchPlatformApp("minimal", "Launched");
526 for (chrome::BrowserIterator it
; !it
.done(); it
.Next())
527 it
->window()->Close();
529 EXPECT_TRUE(chrome::WillKeepAlive());
530 GetFirstAppWindow()->Hide();
531 EXPECT_FALSE(chrome::WillKeepAlive());
534 // A window that is hidden but re-shown should still keep Chrome alive.
535 IN_PROC_BROWSER_TEST_F(AppWindowHiddenKeepAliveTest
, ShownThenHiddenThenShown
) {
536 LoadAndLaunchPlatformApp("minimal", "Launched");
537 AppWindow
* app_window
= GetFirstAppWindow();
539 app_window
->Show(AppWindow::SHOW_ACTIVE
);
541 EXPECT_TRUE(chrome::WillKeepAlive());
542 for (chrome::BrowserIterator it
; !it
.done(); it
.Next())
543 it
->window()->Close();
544 EXPECT_TRUE(chrome::WillKeepAlive());
545 app_window
->GetBaseWindow()->Close();
548 // A window that is created hidden and stays hidden should not keep Chrome
550 IN_PROC_BROWSER_TEST_F(AppWindowHiddenKeepAliveTest
, StaysHidden
) {
551 LoadAndLaunchPlatformApp("hidden", "Launched");
552 AppWindow
* app_window
= GetFirstAppWindow();
553 EXPECT_TRUE(app_window
->is_hidden());
555 for (chrome::BrowserIterator it
; !it
.done(); it
.Next())
556 it
->window()->Close();
557 // This will time out if the command above does not terminate Chrome.
558 content::RunMessageLoop();
561 // A window that is created hidden but shown soon after should keep Chrome
563 IN_PROC_BROWSER_TEST_F(AppWindowHiddenKeepAliveTest
, HiddenThenShown
) {
564 ExtensionTestMessageListener
launched_listener("Launched", true);
565 LoadAndLaunchPlatformApp("hidden_then_shown", &launched_listener
);
566 AppWindow
* app_window
= GetFirstAppWindow();
567 EXPECT_TRUE(app_window
->is_hidden());
569 // Close all browser windows.
570 for (chrome::BrowserIterator it
; !it
.done(); it
.Next())
571 it
->window()->Close();
573 // The app window will show after 3 seconds.
574 ExtensionTestMessageListener
shown_listener("Shown", false);
575 launched_listener
.Reply("");
576 EXPECT_TRUE(shown_listener
.WaitUntilSatisfied());
577 EXPECT_FALSE(app_window
->is_hidden());
578 EXPECT_TRUE(chrome::WillKeepAlive());
579 app_window
->GetBaseWindow()->Close();