blob: 2731ce3cfe198339fee1a3dda242cf8e73597075 [file] [log] [blame]
// 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.
#import <Cocoa/Cocoa.h>
#include <memory>
#include "base/command_line.h"
#import "base/mac/scoped_nsobject.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/permissions/permission_request_manager_test_api.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "ui/base/test/ui_controls.h"
#import "ui/base/test/windowed_nsnotification_observer.h"
#include "ui/base/ui_base_features.h"
#import "ui/events/test/cocoa_test_event_utils.h"
class PermissionBubbleInteractiveUITest : public InProcessBrowserTest {
public:
PermissionBubbleInteractiveUITest() {}
void EnsureWindowActive(NSWindow* window, const char* message) {
SCOPED_TRACE(message);
EXPECT_TRUE(window);
// Activation is asynchronous on Mac. If the window didn't become active,
// wait for it.
if (![window isKeyWindow]) {
base::scoped_nsobject<WindowedNSNotificationObserver> waiter(
[[WindowedNSNotificationObserver alloc]
initForNotification:NSWindowDidBecomeKeyNotification
object:window]);
[waiter wait];
}
EXPECT_TRUE([window isKeyWindow]);
// Whether or not we had to wait, flush the run loop. This ensures any
// asynchronous close operations have completed.
base::RunLoop().RunUntilIdle();
}
// Send Cmd+keycode in the key window to NSApp.
void SendAccelerator(ui::KeyboardCode keycode, bool shift, bool alt) {
bool control = false;
bool command = true;
// Note that although this takes an NSWindow, it's just used to create the
// NSEvent* which will be dispatched to NSApp (i.e. not NSWindow).
ui_controls::SendKeyPress(
[NSApp keyWindow], keycode, control, shift, alt, command);
}
void SetUpOnMainThread() override {
// Make the browser active (ensures the app can receive key events).
EXPECT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
test_api_ =
std::make_unique<test::PermissionRequestManagerTestApi>(browser());
EXPECT_TRUE(test_api_->manager());
test_api_->AddSimpleRequest(CONTENT_SETTINGS_TYPE_GEOLOCATION);
EXPECT_TRUE([browser()->window()->GetNativeWindow().GetNativeNSWindow()
isKeyWindow]);
// The PermissionRequestManager displays prompts asynchronously.
base::RunLoop().RunUntilIdle();
// The bubble should steal key focus when shown.
EnsureWindowActive(test_api_->GetPromptWindow().GetNativeNSWindow(),
"show permission bubble");
}
protected:
std::unique_ptr<test::PermissionRequestManagerTestApi> test_api_;
base::test::ScopedFeatureList scoped_feature_list_;
private:
DISALLOW_COPY_AND_ASSIGN(PermissionBubbleInteractiveUITest);
};
// There is only one tab. Cmd+w will close it along with the browser window.
IN_PROC_BROWSER_TEST_F(PermissionBubbleInteractiveUITest, CmdWClosesWindow) {
base::scoped_nsobject<NSWindow> browser_window(
browser()->window()->GetNativeWindow().GetNativeNSWindow(),
base::scoped_policy::RETAIN);
EXPECT_TRUE([browser_window isVisible]);
content::WindowedNotificationObserver observer(
chrome::NOTIFICATION_BROWSER_CLOSED, content::Source<Browser>(browser()));
SendAccelerator(ui::VKEY_W, false, false);
// The actual window close happens via a posted task.
EXPECT_TRUE([browser_window isVisible]);
observer.Wait();
EXPECT_FALSE([browser_window isVisible]);
}
// Add a tab, ensure we can switch away and back using Cmd+Alt+Left/Right and
// curly braces.
IN_PROC_BROWSER_TEST_F(PermissionBubbleInteractiveUITest, SwitchTabs) {
NSWindow* browser_window =
browser()->window()->GetNativeWindow().GetNativeNSWindow();
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
EXPECT_TRUE(test_api_->GetPromptWindow());
// Add a blank tab in the foreground.
AddBlankTabAndShow(browser());
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
// The bubble should hide and give focus back to the browser. However, the
// test environment can't guarantee that macOS decides that the Browser window
// is actually the "best" window to activate upon closing the current key
// window. So activate it manually.
[browser_window makeKeyAndOrderFront:nil];
EnsureWindowActive(browser_window, "tab added");
// Prompt is hidden while its tab is not active.
EXPECT_FALSE(test_api_->GetPromptWindow());
// Now a webcontents is active, it gets a first shot at processing the
// accelerator before sending it back unhandled to the browser via IPC. That's
// all a bit much to handle in a test, so activate the location bar.
chrome::FocusLocationBar(browser());
SendAccelerator(ui::VKEY_LEFT, false, true);
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
// Note we don't need to makeKeyAndOrderFront: the permission window will take
// focus when it is shown again.
EnsureWindowActive(test_api_->GetPromptWindow().GetNativeNSWindow(),
"switched to permission tab with arrow");
EXPECT_TRUE(test_api_->GetPromptWindow());
// Ensure we can switch away with the bubble active.
SendAccelerator(ui::VKEY_RIGHT, false, true);
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
[browser_window makeKeyAndOrderFront:nil];
EnsureWindowActive(browser_window, "switch away with arrow");
EXPECT_FALSE(test_api_->GetPromptWindow());
// Also test switching tabs with curly braces. "VKEY_OEM_4" is
// LeftBracket/Brace on a US keyboard, which ui::MacKeyCodeForWindowsKeyCode
// will map to '{' when shift is passed. Also note there are only two tabs so
// it doesn't matter which direction is taken (it wraps).
chrome::FocusLocationBar(browser());
SendAccelerator(ui::VKEY_OEM_4, true, false);
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
EnsureWindowActive(test_api_->GetPromptWindow().GetNativeNSWindow(),
"switch to permission tab with curly brace");
EXPECT_TRUE(test_api_->GetPromptWindow());
SendAccelerator(ui::VKEY_OEM_4, true, false);
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
[browser_window makeKeyAndOrderFront:nil];
EnsureWindowActive(browser_window, "switch away with curly brace");
EXPECT_FALSE(test_api_->GetPromptWindow());
}