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) && !defined(OS_IOS)
15 #include "base/mac/mac_util.h"
20 #include "ui/aura/window.h"
21 #include "ui/aura/window_tree_host.h"
22 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
23 #include "ui/views/win/hwnd_message_handler_delegate.h"
24 #include "ui/views/win/hwnd_util.h"
27 using extensions::AppWindow
;
28 using extensions::NativeAppWindow
;
30 // Helper class that has to be created in the stack to check if the fullscreen
31 // setting of a NativeWindow has changed since the creation of the object.
32 class FullscreenChangeWaiter
{
34 explicit FullscreenChangeWaiter(NativeAppWindow
* window
)
36 initial_fullscreen_state_(window_
->IsFullscreen()) {}
39 while (initial_fullscreen_state_
== window_
->IsFullscreen())
40 content::RunAllPendingInMessageLoop();
44 NativeAppWindow
* window_
;
45 bool initial_fullscreen_state_
;
47 DISALLOW_COPY_AND_ASSIGN(FullscreenChangeWaiter
);
50 class AppWindowInteractiveTest
: public extensions::PlatformAppBrowserTest
{
52 bool RunAppWindowInteractiveTest(const char* testName
) {
53 ExtensionTestMessageListener
launched_listener("Launched", true);
54 LoadAndLaunchPlatformApp("window_api_interactive", &launched_listener
);
56 extensions::ResultCatcher catcher
;
57 launched_listener
.Reply(testName
);
59 if (!catcher
.GetNextResult()) {
60 message_
= catcher
.message();
67 bool SimulateKeyPress(ui::KeyboardCode key
) {
68 return ui_test_utils::SendKeyPressToWindowSync(
69 GetFirstAppWindow()->GetNativeWindow(),
77 // This method will wait until the application is able to ack a key event.
78 void WaitUntilKeyFocus() {
79 ExtensionTestMessageListener
key_listener("KeyReceived", false);
81 while (!key_listener
.was_satisfied()) {
82 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_Z
));
83 content::RunAllPendingInMessageLoop();
87 // This test is a method so that we can test with each frame type.
88 void TestOuterBoundsHelper(const std::string
& frame_type
);
91 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, ESCLeavesFullscreenWindow
) {
92 // This test is flaky on MacOS 10.6 and 10.9.
93 #if defined(OS_MACOSX) && !defined(OS_IOS)
94 if (base::mac::IsOSSnowLeopard() || base::mac::IsOSMavericks())
98 ExtensionTestMessageListener
launched_listener("Launched", true);
99 LoadAndLaunchPlatformApp("leave_fullscreen", &launched_listener
);
101 // We start by making sure the window is actually focused.
102 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
103 GetFirstAppWindow()->GetNativeWindow()));
105 // When receiving the reply, the application will try to go fullscreen using
106 // the Window API but there is no synchronous way to know if that actually
107 // succeeded. Also, failure will not be notified. A failure case will only be
108 // known with a timeout.
110 FullscreenChangeWaiter
fs_changed(GetFirstAppWindow()->GetBaseWindow());
112 launched_listener
.Reply("window");
117 // Depending on the platform, going fullscreen might create an animation.
118 // We want to make sure that the ESC key we will send next is actually going
119 // to be received and the application might not receive key events during the
120 // animation so we should wait for the key focus to be back.
123 // Same idea as above but for leaving fullscreen. Fullscreen mode should be
124 // left when ESC is received.
126 FullscreenChangeWaiter
fs_changed(GetFirstAppWindow()->GetBaseWindow());
128 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE
));
134 #if defined(OS_MACOSX)
135 // http://crbug.com/406009
136 #define MAYBE_ESCLeavesFullscreenDOM DISABLED_ESCLeavesFullscreenDOM
138 #define MAYBE_ESCLeavesFullscreenDOM ESCLeavesFullscreenDOM
140 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, MAYBE_ESCLeavesFullscreenDOM
) {
141 // This test is flaky on MacOS 10.6.
142 #if defined(OS_MACOSX) && !defined(OS_IOS)
143 if (base::mac::IsOSSnowLeopard())
147 ExtensionTestMessageListener
launched_listener("Launched", true);
148 LoadAndLaunchPlatformApp("leave_fullscreen", &launched_listener
);
150 // We start by making sure the window is actually focused.
151 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
152 GetFirstAppWindow()->GetNativeWindow()));
154 launched_listener
.Reply("dom");
156 // Because the DOM way to go fullscreen requires user gesture, we simulate a
157 // key event to get the window entering in fullscreen mode. The reply will
158 // make the window listen for the key event. The reply will be sent to the
159 // renderer process before the keypress and should be received in that order.
160 // When receiving the key event, the application will try to go fullscreen
161 // using the Window API but there is no synchronous way to know if that
162 // actually succeeded. Also, failure will not be notified. A failure case will
163 // only be known with a timeout.
165 FullscreenChangeWaiter
fs_changed(GetFirstAppWindow()->GetBaseWindow());
168 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_A
));
173 // Depending on the platform, going fullscreen might create an animation.
174 // We want to make sure that the ESC key we will send next is actually going
175 // to be received and the application might not receive key events during the
176 // animation so we should wait for the key focus to be back.
179 // Same idea as above but for leaving fullscreen. Fullscreen mode should be
180 // left when ESC is received.
182 FullscreenChangeWaiter
fs_changed(GetFirstAppWindow()->GetBaseWindow());
184 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE
));
190 #if defined(OS_MACOSX)
191 // http://crbug.com/406009
192 #define MAYBE_ESCDoesNotLeaveFullscreenWindow DISABLED_ESCDoesNotLeaveFullscreenWindow
194 #define MAYBE_ESCDoesNotLeaveFullscreenWindow ESCDoesNotLeaveFullscreenWindow
196 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
,
197 MAYBE_ESCDoesNotLeaveFullscreenWindow
) {
198 // This test is flaky on MacOS 10.6.
199 #if defined(OS_MACOSX) && !defined(OS_IOS)
200 if (base::mac::IsOSSnowLeopard())
204 ExtensionTestMessageListener
launched_listener("Launched", true);
205 LoadAndLaunchPlatformApp("prevent_leave_fullscreen", &launched_listener
);
207 // We start by making sure the window is actually focused.
208 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
209 GetFirstAppWindow()->GetNativeWindow()));
211 // When receiving the reply, the application will try to go fullscreen using
212 // the Window API but there is no synchronous way to know if that actually
213 // succeeded. Also, failure will not be notified. A failure case will only be
214 // known with a timeout.
216 FullscreenChangeWaiter
fs_changed(GetFirstAppWindow()->GetBaseWindow());
218 launched_listener
.Reply("window");
223 // Depending on the platform, going fullscreen might create an animation.
224 // We want to make sure that the ESC key we will send next is actually going
225 // to be received and the application might not receive key events during the
226 // animation so we should wait for the key focus to be back.
229 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE
));
231 ExtensionTestMessageListener
second_key_listener("B_KEY_RECEIVED", false);
233 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_B
));
235 ASSERT_TRUE(second_key_listener
.WaitUntilSatisfied());
237 // We assume that at that point, if we had to leave fullscreen, we should be.
238 // However, by nature, we can not guarantee that and given that we do test
239 // that nothing happens, we might end up with random-success when the feature
241 EXPECT_TRUE(GetFirstAppWindow()->GetBaseWindow()->IsFullscreen());
244 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
,
245 ESCDoesNotLeaveFullscreenDOM
) {
246 // This test is flaky on MacOS 10.6 and 10.9.
247 #if defined(OS_MACOSX) && !defined(OS_IOS)
248 if (base::mac::IsOSSnowLeopard() || base::mac::IsOSMavericks())
252 ExtensionTestMessageListener
launched_listener("Launched", true);
253 LoadAndLaunchPlatformApp("prevent_leave_fullscreen", &launched_listener
);
255 // We start by making sure the window is actually focused.
256 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
257 GetFirstAppWindow()->GetNativeWindow()));
259 launched_listener
.Reply("dom");
261 // Because the DOM way to go fullscreen requires user gesture, we simulate a
262 // key event to get the window entering in fullscreen mode. The reply will
263 // make the window listen for the key event. The reply will be sent to the
264 // renderer process before the keypress and should be received in that order.
265 // When receiving the key event, the application will try to go fullscreen
266 // using the Window API but there is no synchronous way to know if that
267 // actually succeeded. Also, failure will not be notified. A failure case will
268 // only be known with a timeout.
270 FullscreenChangeWaiter
fs_changed(GetFirstAppWindow()->GetBaseWindow());
273 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_A
));
278 // Depending on the platform, going fullscreen might create an animation.
279 // We want to make sure that the ESC key we will send next is actually going
280 // to be received and the application might not receive key events during the
281 // animation so we should wait for the key focus to be back.
284 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE
));
286 ExtensionTestMessageListener
second_key_listener("B_KEY_RECEIVED", false);
288 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_B
));
290 ASSERT_TRUE(second_key_listener
.WaitUntilSatisfied());
292 // We assume that at that point, if we had to leave fullscreen, we should be.
293 // However, by nature, we can not guarantee that and given that we do test
294 // that nothing happens, we might end up with random-success when the feature
296 EXPECT_TRUE(GetFirstAppWindow()->GetBaseWindow()->IsFullscreen());
299 // This test is duplicated from ESCDoesNotLeaveFullscreenWindow.
300 // It runs the same test, but uses the old permission names: 'fullscreen'
301 // and 'overrideEscFullscreen'.
302 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
,
303 ESCDoesNotLeaveFullscreenOldPermission
) {
304 // This test is flaky on MacOS 10.6 and 10.9.
305 #if defined(OS_MACOSX) && !defined(OS_IOS)
306 if (base::mac::IsOSSnowLeopard() || base::mac::IsOSMavericks())
310 ExtensionTestMessageListener
launched_listener("Launched", true);
311 LoadAndLaunchPlatformApp("prevent_leave_fullscreen_old", &launched_listener
);
313 // We start by making sure the window is actually focused.
314 ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
315 GetFirstAppWindow()->GetNativeWindow()));
317 // When receiving the reply, the application will try to go fullscreen using
318 // the Window API but there is no synchronous way to know if that actually
319 // succeeded. Also, failure will not be notified. A failure case will only be
320 // known with a timeout.
322 FullscreenChangeWaiter
fs_changed(GetFirstAppWindow()->GetBaseWindow());
324 launched_listener
.Reply("window");
329 // Depending on the platform, going fullscreen might create an animation.
330 // We want to make sure that the ESC key we will send next is actually going
331 // to be received and the application might not receive key events during the
332 // animation so we should wait for the key focus to be back.
335 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE
));
337 ExtensionTestMessageListener
second_key_listener("B_KEY_RECEIVED", false);
339 ASSERT_TRUE(SimulateKeyPress(ui::VKEY_B
));
341 ASSERT_TRUE(second_key_listener
.WaitUntilSatisfied());
343 // We assume that at that point, if we had to leave fullscreen, we should be.
344 // However, by nature, we can not guarantee that and given that we do test
345 // that nothing happens, we might end up with random-success when the feature
347 EXPECT_TRUE(GetFirstAppWindow()->GetBaseWindow()->IsFullscreen());
350 #if defined(OS_MACOSX) || defined(OS_WIN)
351 // http://crbug.com/404081
352 #define MAYBE_TestInnerBounds DISABLED_TestInnerBounds
354 #define MAYBE_TestInnerBounds TestInnerBounds
356 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, MAYBE_TestInnerBounds
) {
357 ASSERT_TRUE(RunAppWindowInteractiveTest("testInnerBounds")) << message_
;
360 void AppWindowInteractiveTest::TestOuterBoundsHelper(
361 const std::string
& frame_type
) {
362 ExtensionTestMessageListener
launched_listener("Launched", true);
363 const extensions::Extension
* app
=
364 LoadAndLaunchPlatformApp("outer_bounds", &launched_listener
);
366 launched_listener
.Reply(frame_type
);
367 launched_listener
.Reset();
368 ASSERT_TRUE(launched_listener
.WaitUntilSatisfied());
370 AppWindow
* window
= GetFirstAppWindowForApp(app
->id());
371 gfx::Rect window_bounds
;
372 gfx::Size min_size
, max_size
;
375 // Get the bounds from the HWND.
376 HWND hwnd
= views::HWNDForNativeWindow(window
->GetNativeWindow());
378 ::GetWindowRect(hwnd
, &rect
);
379 window_bounds
= gfx::Rect(
380 rect
.left
, rect
.top
, rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
382 // HWNDMessageHandler calls this when responding to WM_GETMINMAXSIZE, so it's
383 // the closest to what the window will see.
384 views::HWNDMessageHandlerDelegate
* host
=
385 static_cast<views::HWNDMessageHandlerDelegate
*>(
386 static_cast<views::DesktopWindowTreeHostWin
*>(
387 aura::WindowTreeHost::GetForAcceleratedWidget(hwnd
)));
388 host
->GetMinMaxSize(&min_size
, &max_size
);
389 // Note that this does not include the the client area insets so we need to
392 host
->GetClientAreaInsets(&insets
);
393 min_size
= gfx::Size(min_size
.width() + insets
.left() + insets
.right(),
394 min_size
.height() + insets
.top() + insets
.bottom());
395 max_size
= gfx::Size(
396 max_size
.width() ? max_size
.width() + insets
.left() + insets
.right() : 0,
397 max_size
.height() ? max_size
.height() + insets
.top() + insets
.bottom()
399 #endif // defined(OS_WIN)
401 // These match the values in the outer_bounds/test.js
402 EXPECT_EQ(gfx::Rect(10, 11, 300, 301), window_bounds
);
403 EXPECT_EQ(window
->GetBaseWindow()->GetBounds(), window_bounds
);
404 EXPECT_EQ(200, min_size
.width());
405 EXPECT_EQ(201, min_size
.height());
406 EXPECT_EQ(400, max_size
.width());
407 EXPECT_EQ(401, max_size
.height());
410 // TODO(jackhou): Make this test work for other OSes.
412 #define MAYBE_TestOuterBoundsFrameChrome DISABLED_TestOuterBoundsFrameChrome
413 #define MAYBE_TestOuterBoundsFrameNone DISABLED_TestOuterBoundsFrameNone
414 #define MAYBE_TestOuterBoundsFrameColor DISABLED_TestOuterBoundsFrameColor
416 #define MAYBE_TestOuterBoundsFrameChrome TestOuterBoundsFrameChrome
417 #define MAYBE_TestOuterBoundsFrameNone TestOuterBoundsFrameNone
418 #define MAYBE_TestOuterBoundsFrameColor TestOuterBoundsFrameColor
421 // Test that the outer bounds match that of the native window.
422 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
,
423 MAYBE_TestOuterBoundsFrameChrome
) {
424 TestOuterBoundsHelper("chrome");
426 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
,
427 MAYBE_TestOuterBoundsFrameNone
) {
428 TestOuterBoundsHelper("none");
430 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
,
431 MAYBE_TestOuterBoundsFrameColor
) {
432 TestOuterBoundsHelper("color");
435 // This test does not work on Linux Aura because ShowInactive() is not
436 // implemented. See http://crbug.com/325142
437 // It also does not work on Windows because of the document being focused even
438 // though the window is not activated. See http://crbug.com/326986
439 // It also does not work on MacOS because ::ShowInactive() ends up behaving like
440 // ::Show() because of Cocoa conventions. See http://crbug.com/326987
441 // Those tests should be disabled on Linux GTK when they are enabled on the
442 // other platforms, see http://crbug.com/328829
443 #if (defined(OS_LINUX) && defined(USE_AURA)) || \
444 defined(OS_WIN) || defined(OS_MACOSX)
445 #define MAYBE_TestCreate DISABLED_TestCreate
446 #define MAYBE_TestShow DISABLED_TestShow
448 #define MAYBE_TestCreate TestCreate
449 #define MAYBE_TestShow TestShow
452 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, MAYBE_TestCreate
) {
453 ASSERT_TRUE(RunAppWindowInteractiveTest("testCreate")) << message_
;
456 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, MAYBE_TestShow
) {
457 ASSERT_TRUE(RunAppWindowInteractiveTest("testShow")) << message_
;
460 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, TestDrawAttention
) {
461 ASSERT_TRUE(RunAppWindowInteractiveTest("testDrawAttention")) << message_
;
464 IN_PROC_BROWSER_TEST_F(AppWindowInteractiveTest
, TestCreateHidden
) {
465 // Created hidden both times.
467 ExtensionTestMessageListener
launched_listener("Launched", true);
468 LoadAndLaunchPlatformApp("hidden_with_id", &launched_listener
);
469 EXPECT_TRUE(launched_listener
.WaitUntilSatisfied());
470 ExtensionTestMessageListener
create_listener_1("Launched", true);
471 launched_listener
.Reply("createHidden");
472 EXPECT_TRUE(create_listener_1
.WaitUntilSatisfied());
473 AppWindow
* app_window
= GetFirstAppWindow();
474 EXPECT_TRUE(app_window
->is_hidden());
475 ExtensionTestMessageListener
create_listener_2("Launched", false);
476 create_listener_1
.Reply("createHidden");
477 EXPECT_TRUE(create_listener_2
.WaitUntilSatisfied());
478 EXPECT_TRUE(app_window
->is_hidden());
479 app_window
->GetBaseWindow()->Close();
482 // Created hidden, then visible. The second create should show the window.
484 ExtensionTestMessageListener
launched_listener("Launched", true);
485 LoadAndLaunchPlatformApp("hidden_with_id", &launched_listener
);
486 EXPECT_TRUE(launched_listener
.WaitUntilSatisfied());
487 ExtensionTestMessageListener
create_listener_1("Launched", true);
488 launched_listener
.Reply("createHidden");
489 EXPECT_TRUE(create_listener_1
.WaitUntilSatisfied());
490 AppWindow
* app_window
= GetFirstAppWindow();
491 EXPECT_TRUE(app_window
->is_hidden());
492 ExtensionTestMessageListener
create_listener_2("Launched", false);
493 create_listener_1
.Reply("createVisible");
494 EXPECT_TRUE(create_listener_2
.WaitUntilSatisfied());
495 EXPECT_FALSE(app_window
->is_hidden());
496 app_window
->GetBaseWindow()->Close();
500 // Only Linux and Windows use keep-alive to determine when to shut down.
501 #if defined(OS_LINUX) || defined(OS_WIN)
503 // In general, hidden windows should not keep Chrome alive. The exception is
504 // when windows are created hidden, we allow the app some time to show the
506 class AppWindowHiddenKeepAliveTest
: public extensions::PlatformAppBrowserTest
{
508 AppWindowHiddenKeepAliveTest() {}
511 DISALLOW_COPY_AND_ASSIGN(AppWindowHiddenKeepAliveTest
);
514 // A window that becomes hidden should not keep Chrome alive.
515 IN_PROC_BROWSER_TEST_F(AppWindowHiddenKeepAliveTest
, ShownThenHidden
) {
516 LoadAndLaunchPlatformApp("minimal", "Launched");
517 for (chrome::BrowserIterator it
; !it
.done(); it
.Next())
518 it
->window()->Close();
520 EXPECT_TRUE(chrome::WillKeepAlive());
521 GetFirstAppWindow()->Hide();
522 EXPECT_FALSE(chrome::WillKeepAlive());
525 // A window that is hidden but re-shown should still keep Chrome alive.
526 IN_PROC_BROWSER_TEST_F(AppWindowHiddenKeepAliveTest
, ShownThenHiddenThenShown
) {
527 LoadAndLaunchPlatformApp("minimal", "Launched");
528 AppWindow
* app_window
= GetFirstAppWindow();
530 app_window
->Show(AppWindow::SHOW_ACTIVE
);
532 EXPECT_TRUE(chrome::WillKeepAlive());
533 for (chrome::BrowserIterator it
; !it
.done(); it
.Next())
534 it
->window()->Close();
535 EXPECT_TRUE(chrome::WillKeepAlive());
536 app_window
->GetBaseWindow()->Close();
539 // A window that is created hidden and stays hidden should not keep Chrome
541 IN_PROC_BROWSER_TEST_F(AppWindowHiddenKeepAliveTest
, StaysHidden
) {
542 LoadAndLaunchPlatformApp("hidden", "Launched");
543 AppWindow
* app_window
= GetFirstAppWindow();
544 EXPECT_TRUE(app_window
->is_hidden());
546 for (chrome::BrowserIterator it
; !it
.done(); it
.Next())
547 it
->window()->Close();
548 // This will time out if the command above does not terminate Chrome.
549 content::RunMessageLoop();
552 // A window that is created hidden but shown soon after should keep Chrome
554 IN_PROC_BROWSER_TEST_F(AppWindowHiddenKeepAliveTest
, HiddenThenShown
) {
555 ExtensionTestMessageListener
launched_listener("Launched", true);
556 LoadAndLaunchPlatformApp("hidden_then_shown", &launched_listener
);
557 AppWindow
* app_window
= GetFirstAppWindow();
558 EXPECT_TRUE(app_window
->is_hidden());
560 // Close all browser windows.
561 for (chrome::BrowserIterator it
; !it
.done(); it
.Next())
562 it
->window()->Close();
564 // The app window will show after 3 seconds.
565 ExtensionTestMessageListener
shown_listener("Shown", false);
566 launched_listener
.Reply("");
567 EXPECT_TRUE(shown_listener
.WaitUntilSatisfied());
568 EXPECT_FALSE(app_window
->is_hidden());
569 EXPECT_TRUE(chrome::WillKeepAlive());
570 app_window
->GetBaseWindow()->Close();