Chromium Code Reviews| Index: chrome/browser/apps/app_window_intercept_all_keys_uitest.cc |
| diff --git a/chrome/browser/apps/app_window_intercept_all_keys_uitest.cc b/chrome/browser/apps/app_window_intercept_all_keys_uitest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d7f2393a58bae125d93fc3da131d6427a7925fdf |
| --- /dev/null |
| +++ b/chrome/browser/apps/app_window_intercept_all_keys_uitest.cc |
| @@ -0,0 +1,390 @@ |
| +// Copyright 2014 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 "base/callback.h" |
| +#include "base/macros.h" |
| +#include "base/strings/stringprintf.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" |
| +#include "testing/gtest/include/gtest/gtest-spi.h" |
| + |
| +using apps::NativeAppWindow; |
| + |
| +class AppWindowInterceptAllKeysTest |
| + : public extensions::PlatformAppBrowserTest { |
| + public: |
| + // Send key to window but does not wait till the key is actually in the input |
| + // queue (like ui_test_utils::SendKeyPressToWindowSync()) as key will not be |
| + // sent to hook when keyboard is intercepted. |
| + bool SimulateKeyPress(ui::KeyboardCode key, |
| + bool control, |
| + bool shift, |
| + bool alt, |
| + bool command) { |
| + LOG(INFO) << "Sending: " << key << " control = " << control << " shift = " |
|
benwells
2014/07/30 02:50:25
Please remove logging here and elsewhere, LOG(INFO
Sriram
2014/07/31 17:00:06
Is LOG(INFO) not allowed even in tests? What is th
benwells
2014/08/01 00:32:43
Yes, DVLOG(1) or VLOG(1) is fine.
|
| + << shift << " alt = " << alt << " command = " << command; |
| + if (!ui_controls::SendKeyPressNotifyWhenDone( |
| + GetFirstAppWindow()->GetNativeWindow(), |
| + key, |
| + control, |
| + shift, |
| + alt, |
| + command, |
| + null_callback_)) { |
| + LOG(INFO) << "Failed to send key to app"; |
|
benwells
2014/07/30 02:50:25
You can use LOG(WARNING) here, but LOG(INFO) shoul
Sriram
2014/07/31 17:00:05
Done.
|
| + message_ = "Failed to send key to app."; |
| + return false; |
| + } |
| + |
| + base::RunLoop().RunUntilIdle(); |
| + return !testing::Test::HasFatalFailure(); |
| + } |
| + |
| + bool SimulateKeyPress(ui::KeyboardCode key) { |
| + return SimulateKeyPress(key, false, false, false, false); |
| + } |
| + |
| + bool SendReply(const char* message) { |
|
benwells
2014/07/30 02:50:25
Can you explain what this is for? There is already
Sriram
2014/07/31 17:00:05
It is using ExtensionTestMessageListener to send t
benwells
2014/08/01 00:32:43
I'm unsure how this is working. In the .js it uses
|
| + return SendKeyEvent(ui::VKEY_A, message); |
| + } |
| + |
| + bool SendKeyEvent(ui::KeyboardCode code, const char* message) { |
| + std::string key_event = base::StringPrintf("KeyReceived: %d", code); |
| + ExtensionTestMessageListener key_listener(key_event, (message != NULL)); |
| + if (!key_listener.was_satisfied()) { |
|
benwells
2014/07/30 02:50:25
Why do you have to check this here? You have just
Sriram
2014/07/31 17:00:05
Done.
|
| + if (!SimulateKeyPress(code)) { |
| + message_ = "Failed to send key to app"; |
| + return false; |
| + } |
| + key_listener.WaitUntilSatisfied(); |
| + if (message) { |
| + key_listener.Reply(message); |
| + } |
| + } |
| + base::RunLoop().RunUntilIdle(); |
|
benwells
2014/07/30 02:50:25
Why do you run the message loop here?
Sriram
2014/07/31 17:00:06
Done.
|
| + LOG(INFO) << "Application ACK-ed keypress"; |
| + return true; |
| + } |
| + |
| + bool WaitForKeyEvent(ui::KeyboardCode code) { |
| + return SendKeyEvent(code, NULL); |
| + } |
| + |
| + // This method will wait until the application is able to ack a key event. |
| + bool WaitUntilKeyFocus() { return WaitForKeyEvent(ui::VKEY_Z); } |
|
benwells
2014/07/30 02:50:25
I'm not sure how this causes focus to be given to
Sriram
2014/07/31 17:00:06
Intermediate iteration changed focus but not any m
|
| + |
| + ExtensionTestMessageListener* LoadApplication(const char* app_path) { |
| + LOG(INFO) << "Launching app = " << app_path; |
| + scoped_ptr<ExtensionTestMessageListener> launched_listener( |
|
benwells
2014/07/30 02:50:24
Why is this a scoped_ptr?
Oh, now I see. This cod
Sriram
2014/07/31 17:00:06
Done.
|
| + new ExtensionTestMessageListener("Launched", true)); |
| + LoadAndLaunchPlatformApp(app_path, launched_listener.get()); |
| + |
| + LOG(INFO) << "Waiting for 'Launched' response"; |
| + |
| + if (!launched_listener->WaitUntilSatisfied()) { |
| + message_ = "Did not get the 'Launched' message."; |
| + return NULL; |
| + } |
| + |
| + LOG(INFO) << "Validating that application is in focus"; |
| + // We start by making sure the window is actually focused. |
| + if (!ui_test_utils::ShowAndFocusNativeWindow( |
| + GetFirstAppWindow()->GetNativeWindow())) { |
| + message_ = "App did not get focus."; |
| + return NULL; |
| + } |
| + |
| + LOG(INFO) << "Launched application"; |
| + return launched_listener.release(); |
| + } |
| + |
| + void SendTaskSwitchKeys() { |
| + // Send switch sequence (currently just for windows - will have to update as |
| + // more platform support is added). |
| + |
| + // Use null closure keys cannot be intercepted to validate. |
| + ui_controls::SendKeyPressNotifyWhenDone( |
|
benwells
2014/07/30 02:50:25
Can you use SimulateKeyPress here?
Sriram
2014/07/31 17:00:05
Done.
|
| + GetFirstAppWindow()->GetNativeWindow(), |
| + ui::VKEY_TAB, |
| + false, |
| + false, |
| + true, |
| + false, |
| + null_callback_); |
| + base::RunLoop().RunUntilIdle(); |
| + } |
| + |
| + void ValidateCannotInterceptKeys(const char* app_path, |
| + bool change_intercept, |
| + bool enable_intercept) { |
| + scoped_ptr<ExtensionTestMessageListener> launch_listener( |
| + LoadApplication(app_path)); |
| + |
| + const char* message = ""; |
| + if (change_intercept) { |
| + message = enable_intercept ? "enable" : "disable"; |
| + } |
| + launch_listener->Reply(message); |
| + |
| + ASSERT_TRUE(WaitUntilKeyFocus()) << message_; |
| + |
| + SendTaskSwitchKeys(); |
| + |
| + // Send key and check if it is received. |
| + ASSERT_FALSE(SimulateKeyPress(ui::VKEY_Z)) << message_; |
|
benwells
2014/07/30 02:50:25
Where exactly does this fail? Does it timeout?
Sriram
2014/07/31 17:00:06
SendKeyPressNotifyWhenDone() will fail if the wind
|
| + } |
| + |
| + void ValidateInterceptKeys() { |
| + scoped_ptr<ExtensionTestMessageListener> launch_listener( |
| + LoadApplication(app_with_permission_)); |
| + |
| + // setInterceptAllKeys() is asynchronous so wait for response and receiving |
| + // a key back. |
| + launch_listener->Reply("enable"); |
| + |
| + ASSERT_TRUE(WaitUntilKeyFocus()) << message_; |
| + |
| + SendTaskSwitchKeys(); |
| + |
| + // Send key and check if it is received. |
| + ASSERT_TRUE(SimulateKeyPress(ui::VKEY_Z)) << message_; |
| + |
| + ASSERT_TRUE(WaitUntilKeyFocus()) << message_; |
| + } |
| + |
| + protected: |
| + std::string message_; |
| + base::Callback<void(void)> null_callback_; |
| + const char* app_with_permission_ = |
| + "window_api_intercept_all_keys/has_permission"; |
| + const char* app_without_permission_ = |
| + "window_api_intercept_all_keys/no_permission"; |
| +}; |
| + |
| +// Currently this is implemented only for Windows. |
| +#if defined(OS_WIN) |
| +#define MAYBE_GetKeysAfterSwitchSequence GetKeysAfterSwitchSequence |
| +#else |
| +#define MAYBE_GetKeysAfterSwitchSequence DISABLED_GetKeysAfterSwitchSequence |
| +#endif |
| + |
| +// Tests a window continues to keep focus even after application switch key |
| +// sequence is sent when setInterceptAllKeys() is enabled. |
| +IN_PROC_BROWSER_TEST_F(AppWindowInterceptAllKeysTest, |
| + MAYBE_GetKeysAfterSwitchSequence) { |
| + ValidateInterceptKeys(); |
| +} |
| + |
| +// Test to make sure that keys not received after disable. |
| +#if defined(OS_WIN) |
| +#define MAYBE_NoKeysAfterDisableIsCalled NoKeysAfterDisableIsCalled |
| +#else |
| +#define MAYBE_NoKeysAfterDisableIsCalled DISABLED_NoKeysAfterDisableIsCalled |
| +#endif |
| + |
| +IN_PROC_BROWSER_TEST_F(AppWindowInterceptAllKeysTest, |
| + MAYBE_NoKeysAfterDisableIsCalled) { |
| + ValidateInterceptKeys(); |
| + |
| + LOG(INFO) << "Disabling key intercept"; |
| + SendReply("disable"); |
| + |
| + ASSERT_TRUE(WaitUntilKeyFocus()) << message_; |
| + |
| + SendTaskSwitchKeys(); |
| + |
| + // Send key and check if it is received. |
| + ASSERT_FALSE(SimulateKeyPress(ui::VKEY_Z)) << message_; |
| +} |
| + |
| +// Test that calling just disable has no effect in retaining keyboard intercept. |
| +// Currently this is implemented only for Windows. |
| +#if defined(OS_WIN) |
| +#define MAYBE_NoopCallingDisableInterceptAllKeys \ |
| + NoopCallingDisableInterceptAllKeys |
| +#else |
| +#define MAYBE_NoopCallingDisableInterceptAllKeys \ |
| + DISABLED_NoopCallingDisableInterceptAllKeys |
| +#endif |
| + |
| +IN_PROC_BROWSER_TEST_F(AppWindowInterceptAllKeysTest, |
| + MAYBE_NoopCallingDisableInterceptAllKeys) { |
| + ValidateCannotInterceptKeys(app_with_permission_, true, false); |
| +} |
| + |
| +// Test no effect when called without permissions |
| +// Currently this is implemented only for Windows. |
| +#if defined(OS_WIN) |
| +#define MAYBE_NoopCallingEnableWithoutPermission \ |
| + NoopCallingEnableWithoutPermission |
| +#else |
| +#define MAYBE_NoopCallingEnableWithoutPermission \ |
| + DISABLED_NoopCallingEnableWithoutPermission |
| +#endif |
| + |
| +IN_PROC_BROWSER_TEST_F(AppWindowInterceptAllKeysTest, |
| + MAYBE_NoopCallingEnableWithoutPermission) { |
| + ValidateCannotInterceptKeys(app_without_permission_, true, true); |
| +} |
| + |
| +// Test that intercept is disabled by default |
| +#if defined(OS_WIN) |
| +#define MAYBE_InterceptDisabledByDefault InterceptDisabledByDefault |
| +#else |
| +#define MAYBE_InterceptDisabledByDefault DISABLED_InterceptDisabledByDefault |
| +#endif |
| + |
| +IN_PROC_BROWSER_TEST_F(AppWindowInterceptAllKeysTest, |
| + MAYBE_InterceptDisabledByDefault) { |
| + ValidateCannotInterceptKeys(app_with_permission_, false, false); |
| +} |
| + |
| +// Tests that the application cannot be loaded in stable. |
| +IN_PROC_BROWSER_TEST_F(AppWindowInterceptAllKeysTest, CannotLoadOtherThanDev) { |
| + chrome::VersionInfo::Channel version_info[] = { |
| + chrome::VersionInfo::CHANNEL_BETA, chrome::VersionInfo::CHANNEL_STABLE}; |
| + for (unsigned int index = 0; index < arraysize(version_info); index++) { |
| + extensions::ScopedCurrentChannel channel(version_info[index]); |
| + const extensions::Extension* extension; |
| + EXPECT_NONFATAL_FAILURE( |
| + extension = LoadExtension(test_data_dir_.AppendASCII("platform_apps") |
| + .AppendASCII(app_with_permission_)), |
| + ""); |
| + |
| + LOG(INFO) << "Finished loading extension"; |
| + |
| + ASSERT_TRUE(extension == NULL) << "Application loaded in" |
| + << version_info[index] |
| + << " while permission does not exist"; |
| + } |
| +} |
| + |
| +// Inject different keyboard combos and make sure that the app get them all. |
| +#if defined(OS_WIN) |
| +#define MAYBE_ValidateKeyEvent ValidateKeyEvent |
| +#else |
| +#define MAYBE_ValidateKeyEvent DISABLED_ValidateKeyEvent |
| +#endif |
| + |
| +namespace { |
| +// Maximum lenght of the result array in KeyEventTestData structure. |
| +const size_t kMaxResultLength = 10; |
| + |
| +// A structure holding test data of a keyboard event. |
| +// Each keyboard event may generate multiple result strings representing |
| +// the result of keydown, keypress, keyup and textInput events. |
| +// For keydown, keypress and keyup events, the format of the result string is: |
| +// <type> <keyCode> <charCode> <ctrlKey> <shiftKey> <altKey> <commandKey> |
| +// where <type> may be 'D' (keydown), 'P' (keypress) or 'U' (keyup). |
| +// <ctrlKey>, <shiftKey> <altKey> and <commandKey> are boolean value indicating |
| +// the state of corresponding modifier key. |
| +struct KeyEventTestData { |
| + ui::KeyboardCode key; |
| + bool control; |
| + bool shift; |
| + bool alt; |
| + bool command; |
| + |
| + int result_length; |
| + const char* const result[kMaxResultLength]; |
| +}; |
| +} // namespace |
| + |
| +IN_PROC_BROWSER_TEST_F(AppWindowInterceptAllKeysTest, MAYBE_ValidateKeyEvent) { |
| + // Launch the app |
| + ValidateInterceptKeys(); |
| + |
| + static const KeyEventTestData kValidateKeyEvents[] = { |
| + // a |
| + {ui::VKEY_A, |
| + false, |
| + false, |
| + false, |
| + false, |
| + 3, |
| + {"D 65 0 false false false false", |
| + "P 97 97 false false false false", |
| + "U 65 0 false false false false"}}, |
| + // shift+a |
| + {ui::VKEY_A, |
| + false, |
| + true, |
| + false, |
| + false, |
| + 5, |
| + {"D 16 0 false true false false", |
| + "D 65 0 false true false false", |
| + "P 65 65 false true false false", |
| + "U 65 0 false true false false", |
| + "U 16 0 false true false false"}}, |
|
benwells
2014/07/30 02:50:25
Can this data be inferred before checking instead
Sriram
2014/07/31 17:00:06
Not sure if there is a clean way as the only way I
|
| + // ctrl+f which has aceelerator binding should also reuslt in all keys |
|
benwells
2014/07/30 02:50:25
Nit: spelling (aceelerator, reuslt).
Sriram
2014/07/31 17:00:05
Done.
|
| + // being |
| + // sent. |
| + {ui::VKEY_F, |
| + true, |
| + false, |
| + false, |
| + false, |
| + 5, |
| + {"D 17 0 true false false false", |
| + "D 70 0 true false false false", |
| + "P 6 6 true false false false", |
| + "U 70 0 true false false false", |
| + "U 17 0 true false false false"}}, |
| + // ctrl+z |
| + {ui::VKEY_Z, |
| + true, |
| + false, |
| + false, |
| + false, |
| + 5, |
| + {"D 17 0 true false false false", |
| + "D 90 0 true false false false", |
| + "P 26 26 true false false false", |
| + "U 90 0 true false false false", |
| + "U 17 0 true false false false"}}, |
| + // alt+f |
| + {ui::VKEY_F, |
| + false, |
| + false, |
| + true, |
| + false, |
| + 4, |
| + {"D 18 0 false false true false", |
| + "D 70 0 false false true false", |
| + "U 70 0 false false true false", |
| + "U 18 0 false false true false"}}, |
| + // make sure both left and right shift makes it across |
| + {ui::VKEY_RSHIFT, |
| + false, |
| + false, |
| + false, |
| + false, |
| + 2, |
| + {"D 16 0 false true false false", |
| + "U 16 0 false true false false"}}, |
| + }; |
| + |
| + LOG(INFO) << "Starting keyboard input test"; |
| + |
| + for (unsigned int index = 0; index < arraysize(kValidateKeyEvents); index++) { |
| + // create all the event listeners needed |
| + const KeyEventTestData* current_event = &kValidateKeyEvents[index]; |
| + scoped_ptr<ExtensionTestMessageListener> listeners[kMaxResultLength]; |
| + for (int i = 0; i < current_event->result_length; i++) { |
| + listeners[i].reset( |
| + new ExtensionTestMessageListener(current_event->result[i], false)); |
| + } |
| + ASSERT_TRUE(SimulateKeyPress(current_event->key, |
| + current_event->control, |
| + current_event->shift, |
| + current_event->alt, |
| + current_event->command)); |
| + for (int i = 0; i < current_event->result_length; i++) { |
| + EXPECT_TRUE(listeners[i]->WaitUntilSatisfied()); |
| + } |
| + } |
| +} |