| Index: chrome/browser/ui/browser_command_controller_interactive_browsertest.cc
|
| diff --git a/chrome/browser/ui/browser_command_controller_interactive_browsertest.cc b/chrome/browser/ui/browser_command_controller_interactive_browsertest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9bec796b38b4ee137e8e92e9cfbe9dc427089036
|
| --- /dev/null
|
| +++ b/chrome/browser/ui/browser_command_controller_interactive_browsertest.cc
|
| @@ -0,0 +1,336 @@
|
| +// Copyright 2017 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 <memory>
|
| +#include <string>
|
| +
|
| +#include "base/macros.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/strings/string_util.h"
|
| +#include "build/build_config.h"
|
| +#include "chrome/app/chrome_command_ids.h"
|
| +#include "chrome/browser/chrome_notification_types.h"
|
| +#include "chrome/browser/ui/browser.h"
|
| +#include "chrome/browser/ui/browser_commands.h"
|
| +#include "chrome/browser/ui/tabs/tab_strip_model.h"
|
| +#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
|
| +#include "chrome/test/base/in_process_browser_test.h"
|
| +#include "chrome/test/base/interactive_test_utils.h"
|
| +#include "content/public/browser/notification_service.h"
|
| +#include "content/public/test/browser_test_utils.h"
|
| +#include "content/public/test/test_utils.h"
|
| +#include "ui/events/keycodes/dom/keycode_converter.h"
|
| +#include "ui/events/keycodes/keyboard_code_conversion.h"
|
| +#include "ui/events/keycodes/keyboard_codes.h"
|
| +#include "url/gurl.h"
|
| +#include "url/url_constants.h"
|
| +
|
| +#if defined(OS_MACOSX)
|
| +#include "base/mac/mac_util.h"
|
| +#endif
|
| +
|
| +namespace {
|
| +// The html file to receive key events, prevent defaults and export all the
|
| +// events with "getKeyEventReport()" function. It has two magic keys: pressing
|
| +// "S" to enter fullscreen mode; pressing "X" to indicate the end of all the
|
| +// keys (see FinishTestAndVerifyResult() function).
|
| +constexpr char kFullscreenKeyboardLockHTML[] = "/fullscreen_keyboardlock.html";
|
| +
|
| +// On MacOSX command key is used for most of the shortcuts, so replace it with
|
| +// control to reduce the complexity of comparison of the results.
|
| +void NormalizeMetaKeyForMacOS(std::string* output) {
|
| +#if defined(OS_MACOSX)
|
| + base::ReplaceSubstringsAfterOffset(output, 0, "MetaLeft", "ControlLeft");
|
| +#endif
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +class BrowserCommandControllerInteractiveTest : public InProcessBrowserTest {
|
| + public:
|
| + BrowserCommandControllerInteractiveTest() = default;
|
| + ~BrowserCommandControllerInteractiveTest() override = default;
|
| +
|
| + protected:
|
| + // Starts the test page and waits for it to be loaded.
|
| + void StartTestPage();
|
| +
|
| + // Sends a control or command + |key| shortcut to the focused window. Shift
|
| + // modifier will be added if |shift| is true.
|
| + void SendShortcut(ui::KeyboardCode key, bool shift = false);
|
| +
|
| + // Sends a control or command + shift + |key| shortcut to the focused window.
|
| + void SendShiftShortcut(ui::KeyboardCode key);
|
| +
|
| + // Sends a fullscreen shortcut to the focused window and wait for the
|
| + // operation to take effect.
|
| + void SendFullscreenShortcutAndWait();
|
| +
|
| + // Sends a KeyS to the focused window to trigger JavaScript fullscreen and
|
| + // wait for the operation to take effect.
|
| + void SendJsFullscreenShortcutAndWait();
|
| +
|
| + // Sends an ESC to the focused window.
|
| + void SendEscape();
|
| +
|
| + // Sends an ESC to the focused window to exit JavaScript fullscreen and wait
|
| + // for the operation to take effect.
|
| + void SendEscapeAndWaitForExitingFullscreen();
|
| +
|
| + // Sends a set of preventable shortcuts to the web page.
|
| + void SendShortcutsInFullscreen();
|
| +
|
| + // Sends a magic KeyX to the focused window to stop the test case, receives
|
| + // the result and verifies if it is equal to |expected_result_|.
|
| + void FinishTestAndVerifyResult();
|
| +
|
| + private:
|
| + void SetUpOnMainThread() override;
|
| +
|
| + // The expected output from the web page. This string is generated by
|
| + // appending key presses from Send* functions above.
|
| + std::string expected_result_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(BrowserCommandControllerInteractiveTest);
|
| +};
|
| +
|
| +void BrowserCommandControllerInteractiveTest::StartTestPage() {
|
| + ASSERT_TRUE(embedded_test_server()->Start());
|
| + // Ensures the initial states.
|
| + ASSERT_EQ(1, browser()->tab_strip_model()->count());
|
| + ASSERT_EQ(0, browser()->tab_strip_model()->active_index());
|
| + ASSERT_EQ(1U, BrowserList::GetInstance()->size());
|
| + // Add a second tab for counting and focus purposes.
|
| + AddTabAtIndex(1, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_LINK);
|
| + ASSERT_EQ(2, browser()->tab_strip_model()->count());
|
| + ASSERT_EQ(1U, BrowserList::GetInstance()->size());
|
| +
|
| + ui_test_utils::NavigateToURLWithDisposition(
|
| + browser(), embedded_test_server()->GetURL(kFullscreenKeyboardLockHTML),
|
| + WindowOpenDisposition::CURRENT_TAB,
|
| + ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
|
| +}
|
| +
|
| +void BrowserCommandControllerInteractiveTest::SendShortcut(
|
| + ui::KeyboardCode key,
|
| + bool shift /* = false */) {
|
| +#if defined(OS_MACOSX)
|
| + const bool control_modifier = false;
|
| + const bool command_modifier = true;
|
| +#else
|
| + const bool control_modifier = true;
|
| + const bool command_modifier = false;
|
| +#endif
|
| + ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, control_modifier,
|
| + shift, false, command_modifier));
|
| +
|
| + expected_result_ += ui::KeycodeConverter::DomCodeToCodeString(
|
| + ui::UsLayoutKeyboardCodeToDomCode(key));
|
| + expected_result_ += " ctrl:";
|
| + expected_result_ += control_modifier ? "true" : "false";
|
| + expected_result_ += " shift:";
|
| + expected_result_ += shift ? "true" : "false";
|
| + expected_result_ += " alt:false";
|
| + expected_result_ += " meta:";
|
| + expected_result_ += command_modifier ? "true" : "false";
|
| + expected_result_ += '\n';
|
| +}
|
| +
|
| +void BrowserCommandControllerInteractiveTest::SendShiftShortcut(
|
| + ui::KeyboardCode key) {
|
| + ASSERT_NO_FATAL_FAILURE(SendShortcut(key, true));
|
| +}
|
| +
|
| +void BrowserCommandControllerInteractiveTest::SendFullscreenShortcutAndWait() {
|
| + // On MacOSX, entering and exiting fullscreen are not synchronous. So we wait
|
| + // for the observer to notice the change of fullscreen state.
|
| + content::WindowedNotificationObserver observer(
|
| + chrome::NOTIFICATION_FULLSCREEN_CHANGED,
|
| + content::NotificationService::AllSources());
|
| + // Enter fullscreen.
|
| +#if defined(OS_MACOSX)
|
| + // On MACOSX, Command + Control + F is used.
|
| + ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_F, true,
|
| + false, false, true));
|
| +#elif defined(OS_CHROMEOS)
|
| + // A dedicated fullscreen key is used on Chrome OS, so send a fullscreen
|
| + // command directly instead, to avoid constructing the key press.
|
| + ASSERT_TRUE(chrome::ExecuteCommand(browser(), IDC_FULLSCREEN));
|
| +#else
|
| + ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_F11, false,
|
| + false, false, false));
|
| +#endif
|
| +
|
| + observer.Wait();
|
| +}
|
| +
|
| +void BrowserCommandControllerInteractiveTest::
|
| + SendJsFullscreenShortcutAndWait() {
|
| + content::WindowedNotificationObserver observer(
|
| + chrome::NOTIFICATION_FULLSCREEN_CHANGED,
|
| + content::NotificationService::AllSources());
|
| + ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_S, false,
|
| + false, false, false));
|
| + expected_result_ += "KeyS ctrl:false shift:false alt:false meta:false\n";
|
| + observer.Wait();
|
| +}
|
| +
|
| +void BrowserCommandControllerInteractiveTest::SendEscape() {
|
| + ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_ESCAPE, false,
|
| + false, false, false));
|
| + expected_result_ += "Escape ctrl:false shift:false alt:false meta:false\n";
|
| +}
|
| +
|
| +void BrowserCommandControllerInteractiveTest ::
|
| + SendEscapeAndWaitForExitingFullscreen() {
|
| + content::WindowedNotificationObserver observer(
|
| + chrome::NOTIFICATION_FULLSCREEN_CHANGED,
|
| + content::NotificationService::AllSources());
|
| + ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_ESCAPE, false,
|
| + false, false, false));
|
| + observer.Wait();
|
| +}
|
| +
|
| +void BrowserCommandControllerInteractiveTest::SendShortcutsInFullscreen() {
|
| + const int initial_active_index = browser()->tab_strip_model()->active_index();
|
| + const int initial_tab_count = browser()->tab_strip_model()->count();
|
| + const size_t initial_browser_count = BrowserList::GetInstance()->size();
|
| + // The tab should not be closed.
|
| + ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_W));
|
| + ASSERT_EQ(initial_tab_count, browser()->tab_strip_model()->count());
|
| + // The window should not be closed.
|
| + ASSERT_NO_FATAL_FAILURE(SendShiftShortcut(ui::VKEY_W));
|
| + ASSERT_EQ(initial_browser_count, BrowserList::GetInstance()->size());
|
| + // TODO(zijiehe): ChromeOS incorrectly handles these;
|
| + // see http://crbug.com/737307.
|
| +#if !defined(OS_CHROMEOS)
|
| + // A new tab should not be created.
|
| + ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_T));
|
| + ASSERT_EQ(initial_tab_count, browser()->tab_strip_model()->count());
|
| + // A new window should not be created.
|
| + ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_N));
|
| + ASSERT_EQ(initial_browser_count, BrowserList::GetInstance()->size());
|
| + // A new incognito window should not be created.
|
| + ASSERT_NO_FATAL_FAILURE(SendShiftShortcut(ui::VKEY_N));
|
| + ASSERT_EQ(initial_browser_count, BrowserList::GetInstance()->size());
|
| + // Last closed tab should not be restored.
|
| + ASSERT_NO_FATAL_FAILURE(SendShiftShortcut(ui::VKEY_T));
|
| + ASSERT_EQ(initial_tab_count, browser()->tab_strip_model()->count());
|
| +#endif
|
| + // Browser should not switch to the next tab.
|
| + ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_TAB));
|
| + ASSERT_EQ(initial_active_index, browser()->tab_strip_model()->active_index());
|
| + // Browser should not switch to the previous tab.
|
| + ASSERT_NO_FATAL_FAILURE(SendShiftShortcut(ui::VKEY_TAB));
|
| + ASSERT_EQ(initial_active_index, browser()->tab_strip_model()->active_index());
|
| +}
|
| +
|
| +void BrowserCommandControllerInteractiveTest::FinishTestAndVerifyResult() {
|
| + // The renderer process receives key events through IPC channel,
|
| + // SendKeyPressSync() cannot guarantee the JS has processed the key event it
|
| + // sent. So we sent a KeyX to the webpage to indicate the end of the test
|
| + // case. After processing this key event, web page is safe to send the record
|
| + // back through window.domAutomationController.
|
| + EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_X, false,
|
| + false, false, false));
|
| + expected_result_ += "KeyX ctrl:false shift:false alt:false meta:false";
|
| + std::string result;
|
| + EXPECT_TRUE(content::ExecuteScriptAndExtractString(
|
| + browser()->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost(),
|
| + "getKeyEventReport();", &result));
|
| + NormalizeMetaKeyForMacOS(&result);
|
| + NormalizeMetaKeyForMacOS(&expected_result_);
|
| + base::TrimWhitespaceASCII(result, base::TRIM_ALL, &result);
|
| + ASSERT_EQ(expected_result_, result);
|
| +}
|
| +
|
| +void BrowserCommandControllerInteractiveTest::SetUpOnMainThread() {
|
| + ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
|
| +}
|
| +
|
| +IN_PROC_BROWSER_TEST_F(BrowserCommandControllerInteractiveTest,
|
| + ShortcutsShouldTakeEffectInWindowMode) {
|
| + ASSERT_EQ(1, browser()->tab_strip_model()->count());
|
| + ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_T));
|
| + ASSERT_EQ(2, browser()->tab_strip_model()->count());
|
| + ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_T));
|
| + ASSERT_EQ(3, browser()->tab_strip_model()->count());
|
| + ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_W));
|
| + ASSERT_EQ(2, browser()->tab_strip_model()->count());
|
| + ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_W));
|
| + ASSERT_EQ(1, browser()->tab_strip_model()->count());
|
| + ASSERT_NO_FATAL_FAILURE(SendFullscreenShortcutAndWait());
|
| + ASSERT_TRUE(browser()
|
| + ->exclusive_access_manager()
|
| + ->fullscreen_controller()
|
| + ->IsFullscreenForBrowser());
|
| +}
|
| +
|
| +IN_PROC_BROWSER_TEST_F(BrowserCommandControllerInteractiveTest,
|
| + UnpreservedShortcutsShouldBePreventable) {
|
| + ASSERT_NO_FATAL_FAILURE(StartTestPage());
|
| +
|
| + // The browser print function should be blocked by the web page.
|
| + ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_P));
|
| + // The system print function should be blocked by the web page.
|
| + ASSERT_NO_FATAL_FAILURE(SendShiftShortcut(ui::VKEY_P));
|
| + ASSERT_NO_FATAL_FAILURE(FinishTestAndVerifyResult());
|
| +}
|
| +
|
| +#if defined(OS_MACOSX)
|
| +// TODO(zijiehe): Figure out why this test crashes on Mac OSX. The suspicious
|
| +// command is "SendFullscreenShortcutAndWait()". See, http://crbug.com/738949.
|
| +#define MAYBE_KeyEventsShouldBeConsumedByWebPageInBrowserFullscreen \
|
| + DISABLED_KeyEventsShouldBeConsumedByWebPageInBrowserFullscreen
|
| +#else
|
| +#define MAYBE_KeyEventsShouldBeConsumedByWebPageInBrowserFullscreen \
|
| + KeyEventsShouldBeConsumedByWebPageInBrowserFullscreen
|
| +#endif
|
| +IN_PROC_BROWSER_TEST_F(
|
| + BrowserCommandControllerInteractiveTest,
|
| + MAYBE_KeyEventsShouldBeConsumedByWebPageInBrowserFullscreen) {
|
| + ASSERT_NO_FATAL_FAILURE(StartTestPage());
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(SendFullscreenShortcutAndWait());
|
| + ASSERT_NO_FATAL_FAILURE(SendShortcutsInFullscreen());
|
| + // Current page should not exit browser fullscreen mode.
|
| + ASSERT_NO_FATAL_FAILURE(SendEscape());
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(FinishTestAndVerifyResult());
|
| +}
|
| +
|
| +IN_PROC_BROWSER_TEST_F(
|
| + BrowserCommandControllerInteractiveTest,
|
| + KeyEventsShouldBeConsumedByWebPageInJsFullscreenExceptForEsc) {
|
| + ASSERT_NO_FATAL_FAILURE(StartTestPage());
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(SendJsFullscreenShortcutAndWait());
|
| + ASSERT_NO_FATAL_FAILURE(SendShortcutsInFullscreen());
|
| + // Current page should exit HTML fullscreen mode.
|
| + ASSERT_NO_FATAL_FAILURE(SendEscapeAndWaitForExitingFullscreen());
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(FinishTestAndVerifyResult());
|
| +}
|
| +
|
| +IN_PROC_BROWSER_TEST_F(
|
| + BrowserCommandControllerInteractiveTest,
|
| + KeyEventsShouldBeConsumedByWebPageInJsFullscreenExceptForF11) {
|
| + ASSERT_NO_FATAL_FAILURE(StartTestPage());
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(SendJsFullscreenShortcutAndWait());
|
| + ASSERT_NO_FATAL_FAILURE(SendShortcutsInFullscreen());
|
| +#if defined(OS_MACOSX)
|
| + // On 10.9 or earlier, sending the exit fullscreen shortcut will crash the
|
| + // binary. See http://crbug.com/740250.
|
| + if (base::mac::IsAtLeastOS10_10()) {
|
| + // Current page should exit browser fullscreen mode.
|
| + ASSERT_NO_FATAL_FAILURE(SendFullscreenShortcutAndWait());
|
| + }
|
| +#else
|
| + // Current page should exit browser fullscreen mode.
|
| + ASSERT_NO_FATAL_FAILURE(SendFullscreenShortcutAndWait());
|
| +#endif
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(FinishTestAndVerifyResult());
|
| +}
|
|
|