blob: 87a068dd60634caf0ea011cff29cb9279b74b0ec [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "base/test/scoped_run_loop_timeout.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "chrome/browser/ash/accessibility/accessibility_manager.h"
#include "chrome/browser/ash/accessibility/magnification_manager.h"
#include "chrome/browser/ash/accessibility/magnifier_type.h"
#include "chrome/browser/policy/policy_test_utils.h"
#include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/ime/fake_text_input_client.h"
#include "ui/base/ime/input_method.h"
namespace policy {
namespace {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
ChromeKeyboardControllerClient* GetEnabledKeyboardClient() {
auto* client = ChromeKeyboardControllerClient::Get();
if (client != nullptr) {
client->SetEnableFlag(keyboard::KeyboardEnableFlag::kTouchEnabled);
}
return client;
}
ui::InputMethod* GetInputMethod(ChromeKeyboardControllerClient* client) {
aura::Window* root_window = client->GetKeyboardWindow()->GetRootWindow();
return root_window != nullptr ? root_window->GetHost()->GetInputMethod()
: nullptr;
}
void Wait(base::TimeDelta timeout) {
base::RunLoop run_loop;
base::OneShotTimer timer;
timer.Start(FROM_HERE, timeout, run_loop.QuitClosure());
run_loop.Run();
timer.Stop();
}
ui::FakeTextInputClient::Options WebTextFieldOptions() {
return {
.type = ui::TEXT_INPUT_TYPE_TEXT,
.mode = ui::TEXT_INPUT_MODE_TEXT,
.flags = ui::TEXT_INPUT_FLAG_SPELLCHECK_ON,
};
}
class KeyboardVisibilityWaiter
: public ChromeKeyboardControllerClient::Observer {
public:
explicit KeyboardVisibilityWaiter(ChromeKeyboardControllerClient* client,
bool visible)
: visible_(visible) {
observer_.Observe(client);
}
KeyboardVisibilityWaiter(const KeyboardVisibilityWaiter&) = delete;
KeyboardVisibilityWaiter& operator=(const KeyboardVisibilityWaiter&) = delete;
~KeyboardVisibilityWaiter() override = default;
void Wait() {
if (observer_.GetSource()->is_keyboard_visible() != visible_) {
run_loop_.Run();
}
}
// ChromeKeyboardControllerClient::Observer
void OnKeyboardVisibilityChanged(bool visible) override {
if (visible == visible_) {
run_loop_.Quit();
}
}
private:
bool visible_;
base::ScopedObservation<ChromeKeyboardControllerClient,
ChromeKeyboardControllerClient::Observer>
observer_{this};
base::RunLoop run_loop_;
};
void WaitUntilKeyboardShown(ChromeKeyboardControllerClient* client) {
KeyboardVisibilityWaiter(client, true).Wait();
}
void WaitUntilKeyboardHidden(ChromeKeyboardControllerClient* client) {
KeyboardVisibilityWaiter(client, false).Wait();
}
#endif
} // namespace
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
class VirtualKeyboardPolicyTest : public PolicyTest {};
class VirtualKeyboardPolicyTestWithServer : public PolicyTest {
public:
VirtualKeyboardPolicyTestWithServer()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
~VirtualKeyboardPolicyTestWithServer() override = default;
void SetUpOnMainThread() override {
PolicyTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
https_server_.ServeFilesFromSourceDirectory(
base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
content::SetupCrossSiteRedirector(&https_server_);
net::test_server::RegisterDefaultHandlers(&https_server_);
ASSERT_TRUE(https_server_.Start());
}
protected:
net::EmbeddedTestServer* https_server() { return &https_server_; }
private:
net::EmbeddedTestServer https_server_;
};
IN_PROC_BROWSER_TEST_F(VirtualKeyboardPolicyTest,
VirtualKeyboardSmartVisibilityEnabledDefault) {
auto* keyboard_client = GetEnabledKeyboardClient();
ASSERT_TRUE(keyboard_client);
ui::InputMethod* input_method = GetInputMethod(keyboard_client);
ASSERT_TRUE(input_method);
ui::FakeTextInputClient text_client(input_method, WebTextFieldOptions());
// Showing the virtual keyboard, losing focus, then focusing again should show
// the virtual keyboard again.
text_client.Focus();
input_method->SetVirtualKeyboardVisibilityIfEnabled(true);
WaitUntilKeyboardShown(keyboard_client);
text_client.Blur();
WaitUntilKeyboardHidden(keyboard_client);
text_client.Focus();
WaitUntilKeyboardShown(keyboard_client);
}
IN_PROC_BROWSER_TEST_F(VirtualKeyboardPolicyTest,
VirtualKeyboardSmartVisibilityEnabledEnabled) {
auto* keyboard_client = GetEnabledKeyboardClient();
ASSERT_TRUE(keyboard_client);
ui::InputMethod* input_method = GetInputMethod(keyboard_client);
ASSERT_TRUE(input_method);
ui::FakeTextInputClient text_client(input_method, WebTextFieldOptions());
PolicyMap policies;
policies.Set(key::kVirtualKeyboardSmartVisibilityEnabled,
POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
base::Value(true), nullptr);
UpdateProviderPolicy(policies);
// Showing the virtual keyboard, losing focus, then focusing again should show
// the virtual keyboard again.
text_client.Focus();
input_method->SetVirtualKeyboardVisibilityIfEnabled(true);
WaitUntilKeyboardShown(keyboard_client);
text_client.Blur();
WaitUntilKeyboardHidden(keyboard_client);
text_client.Focus();
WaitUntilKeyboardShown(keyboard_client);
}
IN_PROC_BROWSER_TEST_F(VirtualKeyboardPolicyTest,
VirtualKeyboardSmartVisibilityEnabledDisabled) {
auto* keyboard_client = GetEnabledKeyboardClient();
ASSERT_TRUE(keyboard_client);
ui::InputMethod* input_method = GetInputMethod(keyboard_client);
ASSERT_TRUE(input_method);
ui::FakeTextInputClient text_client(input_method, WebTextFieldOptions());
PolicyMap policies;
policies.Set(key::kVirtualKeyboardSmartVisibilityEnabled,
POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
base::Value(false), nullptr);
UpdateProviderPolicy(policies);
// Showing the virtual keyboard, losing focus, then focusing again should keep
// the virtual keyboard hidden.
text_client.Focus();
input_method->SetVirtualKeyboardVisibilityIfEnabled(true);
WaitUntilKeyboardShown(keyboard_client);
text_client.Blur();
WaitUntilKeyboardHidden(keyboard_client);
text_client.Focus();
Wait(base::Seconds(1));
EXPECT_FALSE(keyboard_client->is_keyboard_visible());
}
IN_PROC_BROWSER_TEST_F(VirtualKeyboardPolicyTestWithServer,
VirtualKeyboardShowAfterCrossOriginNavigation) {
GURL first_url(
https_server()->GetURL("a.test", "/virtual_keyboard_policy.html"));
GURL second_url(https_server()->GetURL("xorigin.a.test",
"/virtual_keyboard_policy.html"));
auto* keyboard_client = GetEnabledKeyboardClient();
ASSERT_TRUE(keyboard_client);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), first_url));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
// Give the frame user activation.
EXPECT_TRUE(content::ExecJs(web_contents, "() => {}"));
EXPECT_EQ(true,
content::EvalJs(web_contents, "navigator.userActivation.isActive",
content::EXECUTE_SCRIPT_NO_USER_GESTURE));
// The cross-origin renderer-initiated navigation will clear user activation.
ASSERT_TRUE(content::NavigateToURLFromRenderer(web_contents, second_url));
EXPECT_EQ(false,
content::EvalJs(web_contents, "navigator.userActivation.isActive",
content::EXECUTE_SCRIPT_NO_USER_GESTURE));
// The virtual keyboard should still show without user activation because the
// previously navigated page had sticky user activation.
EXPECT_TRUE(content::ExecJs(web_contents, "navigator.virtualKeyboard.show();",
content::EXECUTE_SCRIPT_NO_USER_GESTURE));
WaitUntilKeyboardShown(keyboard_client);
}
#endif
} // namespace policy