diff options
14 files changed, 356 insertions, 0 deletions
diff --git a/apps/shell_window.cc b/apps/shell_window.cc index c50e532..75cd7cd 100644 --- a/apps/shell_window.cc +++ b/apps/shell_window.cc @@ -289,9 +289,39 @@ void ShellWindow::AddNewContents(WebContents* source, initial_pos, user_gesture, was_blocked); } +bool ShellWindow::PreHandleKeyboardEvent( + content::WebContents* source, + const content::NativeWebKeyboardEvent& event, + bool* is_keyboard_shortcut) { + // Here, we can handle a key event before the content gets it. When we are + // fullscreen, we want to allow the user to leave when ESC is pressed. + // However, if the application has the "overrideEscFullscreen" permission, we + // should let it override that behavior. + // ::HandleKeyboardEvent() will only be called if the KeyEvent's default + // action is not prevented. + // Thus, we should handle the KeyEvent here only if the permission is not set. + if (event.windowsKeyCode == ui::VKEY_ESCAPE && + (fullscreen_types_ != FULLSCREEN_TYPE_NONE) && + !extension_->HasAPIPermission(APIPermission::kOverrideEscFullscreen)) { + Restore(); + return true; + } + + return false; +} + void ShellWindow::HandleKeyboardEvent( WebContents* source, const content::NativeWebKeyboardEvent& event) { + // If the window is currently fullscreen, ESC should leave fullscreen. + // If this code is being called for ESC, that means that the KeyEvent's + // default behavior was not prevented by the content. + if (event.windowsKeyCode == ui::VKEY_ESCAPE && + (fullscreen_types_ != FULLSCREEN_TYPE_NONE)) { + Restore(); + return; + } + native_app_window_->HandleKeyboardEvent(event); } diff --git a/apps/shell_window.h b/apps/shell_window.h index 86dafb5..e833f75 100644 --- a/apps/shell_window.h +++ b/apps/shell_window.h @@ -374,6 +374,10 @@ class ShellWindow : public content::NotificationObserver, const gfx::Rect& initial_pos, bool user_gesture, bool* was_blocked) OVERRIDE; + virtual bool PreHandleKeyboardEvent( + content::WebContents* source, + const content::NativeWebKeyboardEvent& event, + bool* is_keyboard_shortcut) OVERRIDE; virtual void HandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) OVERRIDE; diff --git a/chrome/browser/apps/app_interactive_uitest.cc b/chrome/browser/apps/app_interactive_uitest.cc new file mode 100644 index 0000000..8a6c96d --- /dev/null +++ b/chrome/browser/apps/app_interactive_uitest.cc @@ -0,0 +1,186 @@ +// Copyright 2013 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 "apps/ui/native_app_window.h" +#include "chrome/browser/apps/app_browsertest_util.h" +#include "chrome/browser/extensions/extension_test_message_listener.h" +#include "chrome/test/base/interactive_test_utils.h" + +using namespace apps; + +// This test does not work on Linux Aura yet. It might be because the fullscreen +// window is not correctly focused or because key events are not correctly sent. +#if !(defined(OS_LINUX) && defined(USE_AURA)) + +// This test is also highly flaky on MacOS X. +#if !defined(OS_MACOSX) + +// Helper class that has to be created in the stack to check if the fullscreen +// setting of a NativeWindow has changed since the creation of the object. +class FullscreenChangeWaiter { + public: + explicit FullscreenChangeWaiter(NativeAppWindow* window) + : window_(window), + initial_fullscreen_state_(window_->IsFullscreen()) {} + + void Wait() { + while (initial_fullscreen_state_ != window_->IsFullscreen()) + content::RunAllPendingInMessageLoop(); + } + + private: + NativeAppWindow* window_; + bool initial_fullscreen_state_; + + DISALLOW_COPY_AND_ASSIGN(FullscreenChangeWaiter); +}; + +class AppInteractiveTest : public extensions::PlatformAppBrowserTest { + public: + bool SimulateKeyPress(ui::KeyboardCode key) { + return ui_test_utils::SendKeyPressToWindowSync( + GetFirstShellWindow()->GetNativeWindow(), + key, + false, + false, + false, + false); + } +}; + +IN_PROC_BROWSER_TEST_F(AppInteractiveTest, ESCLeavesFullscreenWindow) { + ExtensionTestMessageListener launched_listener("Launched", true); + LoadAndLaunchPlatformApp("leave_fullscreen"); + ASSERT_TRUE(launched_listener.WaitUntilSatisfied()); + + // When receiving the reply, the application will try to go fullscreen using + // the Window API but there is no synchronous way to know if that actually + // succeeded. Also, failure will not be notified. A failure case will only be + // known with a timeout. + { + FullscreenChangeWaiter fs_changed(GetFirstShellWindow()->GetBaseWindow()); + + launched_listener.Reply("window"); + + fs_changed.Wait(); + } + + // Same idea as above but for leaving fullscreen. Fullscreen mode should be + // left when ESC is received. + { + FullscreenChangeWaiter fs_changed(GetFirstShellWindow()->GetBaseWindow()); + + ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE)); + + fs_changed.Wait(); + } +} + +IN_PROC_BROWSER_TEST_F(AppInteractiveTest, ESCLeavesFullscreenDOM) { + ExtensionTestMessageListener launched_listener("Launched", true); + LoadAndLaunchPlatformApp("leave_fullscreen"); + ASSERT_TRUE(launched_listener.WaitUntilSatisfied()); + + launched_listener.Reply("dom"); + + // Because the DOM way to go fullscreen requires user gesture, we simulate a + // key event to get the window entering in fullscreen mode. The reply will + // make the window listen for the key event. The reply will be sent to the + // renderer process before the keypress and should be received in that order. + // When receiving the key event, the application will try to go fullscreen + // using the Window API but there is no synchronous way to know if that + // actually succeeded. Also, failure will not be notified. A failure case will + // only be known with a timeout. + { + FullscreenChangeWaiter fs_changed(GetFirstShellWindow()->GetBaseWindow()); + + ASSERT_TRUE(SimulateKeyPress(ui::VKEY_A)); + + fs_changed.Wait(); + } + + // Same idea as above but for leaving fullscreen. Fullscreen mode should be + // left when ESC is received. + { + FullscreenChangeWaiter fs_changed(GetFirstShellWindow()->GetBaseWindow()); + + ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE)); + + fs_changed.Wait(); + } +} + +IN_PROC_BROWSER_TEST_F(AppInteractiveTest, ESCDoesNotLeaveFullscreenWindow) { + ExtensionTestMessageListener launched_listener("Launched", true); + LoadAndLaunchPlatformApp("prevent_leave_fullscreen"); + ASSERT_TRUE(launched_listener.WaitUntilSatisfied()); + + // When receiving the reply, the application will try to go fullscreen using + // the Window API but there is no synchronous way to know if that actually + // succeeded. Also, failure will not be notified. A failure case will only be + // known with a timeout. + { + FullscreenChangeWaiter fs_changed(GetFirstShellWindow()->GetBaseWindow()); + + launched_listener.Reply("window"); + + fs_changed.Wait(); + } + + ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE)); + + ExtensionTestMessageListener second_key_listener("B_KEY_RECEIVED", false); + + ASSERT_TRUE(SimulateKeyPress(ui::VKEY_B)); + + ASSERT_TRUE(second_key_listener.WaitUntilSatisfied()); + + // We assume that at that point, if we had to leave fullscreen, we should be. + // However, by nature, we can not guarantee that and given that we do test + // that nothing happens, we might end up with random-success when the feature + // is broken. + EXPECT_TRUE(GetFirstShellWindow()->GetBaseWindow()->IsFullscreen()); +} + +IN_PROC_BROWSER_TEST_F(AppInteractiveTest, ESCDoesNotLeaveFullscreenDOM) { + ExtensionTestMessageListener launched_listener("Launched", true); + LoadAndLaunchPlatformApp("prevent_leave_fullscreen"); + ASSERT_TRUE(launched_listener.WaitUntilSatisfied()); + + launched_listener.Reply("dom"); + + // Because the DOM way to go fullscreen requires user gesture, we simulate a + // key event to get the window entering in fullscreen mode. The reply will + // make the window listen for the key event. The reply will be sent to the + // renderer process before the keypress and should be received in that order. + // When receiving the key event, the application will try to go fullscreen + // using the Window API but there is no synchronous way to know if that + // actually succeeded. Also, failure will not be notified. A failure case will + // only be known with a timeout. + { + FullscreenChangeWaiter fs_changed(GetFirstShellWindow()->GetBaseWindow()); + + ASSERT_TRUE(SimulateKeyPress(ui::VKEY_A)); + + fs_changed.Wait(); + } + + ASSERT_TRUE(SimulateKeyPress(ui::VKEY_ESCAPE)); + + ExtensionTestMessageListener second_key_listener("B_KEY_RECEIVED", false); + + ASSERT_TRUE(SimulateKeyPress(ui::VKEY_B)); + + ASSERT_TRUE(second_key_listener.WaitUntilSatisfied()); + + // We assume that at that point, if we had to leave fullscreen, we should be. + // However, by nature, we can not guarantee that and given that we do test + // that nothing happens, we might end up with random-success when the feature + // is broken. + EXPECT_TRUE(GetFirstShellWindow()->GetBaseWindow()->IsFullscreen()); +} + +#endif // !(defined(OS_MACOSX)) + +#endif // !(defined(OS_LINUX) && defined(USE_AURA)) diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index cd8764a..a8eb09f 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -160,6 +160,7 @@ '../ui/views/widget/widget_interactive_uitest.cc', 'browser/apps/app_browsertest_util.cc', 'browser/apps/app_browsertest_util.h', + 'browser/apps/app_interactive_uitest.cc', 'browser/apps/web_view_interactive_browsertest.cc', 'browser/autofill/autofill_interactive_uitest.cc', 'browser/browser_keyevents_browsertest.cc', diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json index c903baa..4ffc8b6 100644 --- a/chrome/common/extensions/api/_permission_features.json +++ b/chrome/common/extensions/api/_permission_features.json @@ -593,6 +593,10 @@ "extension", "legacy_packaged_app", "hosted_app", "platform_app" ] }, + "overrideEscFullscreen": { + "channel": "stable", + "extension_types": ["platform_app"] + }, "echoPrivate": { "channel": "stable", "extension_types": ["extension", "legacy_packaged_app"], diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc index 10dbaef1..3496cdd 100644 --- a/chrome/common/extensions/permissions/chrome_api_permissions.cc +++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc @@ -354,6 +354,7 @@ std::vector<APIPermissionInfo*> ChromeAPIPermissions::GetAllPermissions() { APIPermission::kFullscreen, "fullscreen" }, { APIPermission::kAudio, "audio" }, { APIPermission::kWebRtc, "webrtc" }, + { APIPermission::kOverrideEscFullscreen, "overrideEscFullscreen" }, // Settings override permissions. { APIPermission::kHomepage, "homepage", diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc index 4951782..d81d4fb 100644 --- a/chrome/common/extensions/permissions/permission_set_unittest.cc +++ b/chrome/common/extensions/permissions/permission_set_unittest.cc @@ -683,6 +683,7 @@ TEST(PermissionsTest, PermissionMessages) { skip.insert(APIPermission::kUnlimitedStorage); skip.insert(APIPermission::kWebRtc); skip.insert(APIPermission::kWebView); + skip.insert(APIPermission::kOverrideEscFullscreen); // TODO(erikkay) add a string for this permission. skip.insert(APIPermission::kBackground); diff --git a/chrome/test/data/extensions/platform_apps/leave_fullscreen/main.html b/chrome/test/data/extensions/platform_apps/leave_fullscreen/main.html new file mode 100644 index 0000000..831d97e --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/leave_fullscreen/main.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html> +<head> + <title>fullscreen</title> +</head> +<body> + foobar +</body> +</html> diff --git a/chrome/test/data/extensions/platform_apps/leave_fullscreen/main.js b/chrome/test/data/extensions/platform_apps/leave_fullscreen/main.js new file mode 100644 index 0000000..109bccf --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/leave_fullscreen/main.js @@ -0,0 +1,31 @@ +// Copyright 2013 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. + +chrome.app.runtime.onLaunched.addListener(function() { + chrome.app.window.create('main.html', {}, function(win) { + // The following key events handler should have no effect because the + // application does not have the 'overrideEscFullscreen' permission. + win.contentWindow.document.addEventListener('keydown', function(e) { + e.preventDefault(); + }); + win.contentWindow.document.addEventListener('keyup', function(e) { + e.preventDefault(); + }); + + chrome.test.sendMessage('Launched', function(reply) { + switch (reply) { + case 'window': + win.fullscreen(); + break; + case 'dom': + win.contentWindow.document.addEventListener('keydown', function() { + win.contentWindow.document.removeEventListener('keydown', + arguments.callee); + win.contentWindow.document.body.webkitRequestFullscreen(); + }); + break; + } + }); + }); +}); diff --git a/chrome/test/data/extensions/platform_apps/leave_fullscreen/manifest.json b/chrome/test/data/extensions/platform_apps/leave_fullscreen/manifest.json new file mode 100644 index 0000000..5bf766f --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/leave_fullscreen/manifest.json @@ -0,0 +1,12 @@ +{ + "name": "Test app for leaving fullscreen rules", + "version": "1", + "app": { + "background": { + "scripts": ["main.js"] + } + }, + "permissions": [ + "fullscreen" + ] +} diff --git a/chrome/test/data/extensions/platform_apps/prevent_leave_fullscreen/main.html b/chrome/test/data/extensions/platform_apps/prevent_leave_fullscreen/main.html new file mode 100644 index 0000000..831d97e --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/prevent_leave_fullscreen/main.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html> +<head> + <title>fullscreen</title> +</head> +<body> + foobar +</body> +</html> diff --git a/chrome/test/data/extensions/platform_apps/prevent_leave_fullscreen/main.js b/chrome/test/data/extensions/platform_apps/prevent_leave_fullscreen/main.js new file mode 100644 index 0000000..e206710 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/prevent_leave_fullscreen/main.js @@ -0,0 +1,55 @@ +// Copyright 2013 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. + +chrome.app.runtime.onLaunched.addListener(function() { + chrome.app.window.create('main.html', {}, function(win) { + // The following key events handler will prevent the default behavior for + // the ESC key, thus will prevent the ESC key to leave fullscreen. + win.contentWindow.document.addEventListener('keydown', function(e) { + e.preventDefault(); + }); + win.contentWindow.document.addEventListener('keyup', function(e) { + e.preventDefault(); + }); + + chrome.test.sendMessage('Launched', function(reply) { + var doc = win.contentWindow.document; + + switch (reply) { + case 'window': + doc.addEventListener('keydown', function(e) { + if (e.keyCode != 66) // 'b' + return; + doc.removeEventListener('keydown', arguments.callee); + // We do one trip to the event loop to increase the chances that + // fullscreen could have been left before the message is received. + setTimeout(function() { + chrome.test.sendMessage('B_KEY_RECEIVED'); + }); + }); + win.fullscreen(); + break; + + case 'dom': + doc.addEventListener('keydown', function() { + doc.removeEventListener('keydown', arguments.callee); + + doc.addEventListener('keydown', function(e) { + if (e.keyCode != 66) // 'b' + return; + doc.removeEventListener('keydown', arguments.callee); + // We do one trip to the event loop to increase the chances that + // fullscreen could have been left before the message is received. + setTimeout(function() { + chrome.test.sendMessage('B_KEY_RECEIVED'); + }); + }); + + doc.body.webkitRequestFullscreen(); + }); + break; + } + }); + }); +}); diff --git a/chrome/test/data/extensions/platform_apps/prevent_leave_fullscreen/manifest.json b/chrome/test/data/extensions/platform_apps/prevent_leave_fullscreen/manifest.json new file mode 100644 index 0000000..8cd9d23 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/prevent_leave_fullscreen/manifest.json @@ -0,0 +1,12 @@ +{ + "name": "Test app for leaving fullscreen rules", + "version": "1", + "app": { + "background": { + "scripts": ["main.js"] + } + }, + "permissions": [ + "fullscreen", "overrideEscFullscreen" + ] +} diff --git a/extensions/common/permissions/api_permission.h b/extensions/common/permissions/api_permission.h index e0054ae..78fa428 100644 --- a/extensions/common/permissions/api_permission.h +++ b/extensions/common/permissions/api_permission.h @@ -112,6 +112,7 @@ class APIPermission { kNativeMessaging, kNetworkingPrivate, kNotification, + kOverrideEscFullscreen, kPageCapture, kPointerLock, kPlugin, |