// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/accessibility/autoclick_test_utils.h"
#include "ash/accessibility/autoclick/autoclick_controller.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/accessibility_controller_enums.h"
#include "ash/shell.h"
#include "ash/system/accessibility/autoclick_menu_bubble_controller.h"
#include "ash/system/accessibility/autoclick_menu_view.h"
#include "base/check_op.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "chrome/browser/ash/accessibility/accessibility_manager.h"
#include "chrome/browser/ash/accessibility/accessibility_test_utils.h"
#include "chrome/browser/ash/accessibility/automation_test_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/extension_constants.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "extensions/browser/browsertest_util.h"
#include "extensions/browser/extension_host_test_helper.h"
#include "ui/events/test/event_generator.h"
namespace {
const int kDefaultDelay = 5;
} // namespace
namespace ash {
AutoclickTestUtils::AutoclickTestUtils(Profile* profile) {
CHECK_EQ(false, AccessibilityManager::Get()->IsAutoclickEnabled());
profile_ = profile;
console_observer_ = std::make_unique<ExtensionConsoleErrorObserver>(
profile_, extension_misc::kAccessibilityCommonExtensionId);
SetAutoclickDelayMs(kDefaultDelay);
pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
pref_change_registrar_->Init(profile_->GetPrefs());
pref_change_registrar_->Add(
prefs::kAccessibilityAutoclickEventType,
base::BindRepeating(&AutoclickTestUtils::OnEventTypePrefChanged,
GetWeakPtr()));
automation_utils_ = std::make_unique<AutomationTestUtils>(
extension_misc::kAccessibilityCommonExtensionId);
}
AutoclickTestUtils::~AutoclickTestUtils() {
pref_change_registrar_.reset();
}
void AutoclickTestUtils::LoadAutoclick(bool install_automation_utils) {
extensions::ExtensionHostTestHelper host_helper(
profile_, extension_misc::kAccessibilityCommonExtensionId);
AccessibilityManager::Get()->EnableAutoclick(true);
Shell::Get()
->autoclick_controller()
->GetMenuBubbleControllerForTesting()
->SetAnimateForTesting(false);
host_helper.WaitForHostCompletedFirstLoad();
WaitForAutoclickReady();
if (install_automation_utils) {
automation_utils_->SetUpTestSupport();
}
}
void AutoclickTestUtils::SetAutoclickDelayMs(int ms) {
profile_->GetPrefs()->SetInteger(prefs::kAccessibilityAutoclickDelayMs, ms);
profile_->GetPrefs()->CommitPendingWrite();
}
void AutoclickTestUtils::SetAutoclickEventTypeWithHover(
ui::test::EventGenerator* generator,
AutoclickEventType type) {
// Check if we already have the right type selected.
if (profile_->GetPrefs()->GetInteger(
prefs::kAccessibilityAutoclickEventType) == static_cast<int>(type)) {
return;
}
// Change the Autoclick delay to a value we know will work for this method
// (in case it was set to a very large value before this method was called).
int old_delay =
profile_->GetPrefs()->GetInteger(prefs::kAccessibilityAutoclickDelayMs);
SetAutoclickDelayMs(kDefaultDelay);
// Find the menu button.
AutoclickMenuView::ButtonId button_id;
switch (type) {
case AutoclickEventType::kLeftClick:
button_id = AutoclickMenuView::ButtonId::kLeftClick;
break;
case AutoclickEventType::kRightClick:
button_id = AutoclickMenuView::ButtonId::kRightClick;
break;
case AutoclickEventType::kDoubleClick:
button_id = AutoclickMenuView::ButtonId::kDoubleClick;
break;
case AutoclickEventType::kDragAndDrop:
button_id = AutoclickMenuView::ButtonId::kDragAndDrop;
break;
case AutoclickEventType::kScroll:
button_id = AutoclickMenuView::ButtonId::kScroll;
break;
case AutoclickEventType::kNoAction:
button_id = AutoclickMenuView::ButtonId::kPause;
break;
}
AutoclickMenuView* menu_view = Shell::Get()
->autoclick_controller()
->GetMenuBubbleControllerForTesting()
->menu_view_;
CHECK_NE(nullptr, menu_view);
auto* button_view = menu_view->GetViewByID(static_cast<int>(button_id));
CHECK_NE(nullptr, button_view);
// Hover over it.
const gfx::Rect bounds = button_view->GetBoundsInScreen();
generator->MoveMouseTo(bounds.CenterPoint());
// Wait for the pref change, indicating the button was pressed.
base::RunLoop runner;
pref_change_waiter_ = runner.QuitClosure();
runner.Run();
// Restore the delay to its previous value.
SetAutoclickDelayMs(old_delay);
}
void AutoclickTestUtils::WaitForPageLoad(const std::string& url) {
automation_utils_->WaitForPageLoad(url);
}
void AutoclickTestUtils::WaitForTextSelectionChangedEvent() {
automation_utils_->WaitForTextSelectionChangedEvent();
}
void AutoclickTestUtils::HoverOverHtmlElement(
ui::test::EventGenerator* generator,
const std::string& name,
const std::string& role) {
const gfx::Rect bounds = automation_utils_->GetNodeBoundsInRoot(name, role);
generator->MoveMouseTo(bounds.CenterPoint());
}
void AutoclickTestUtils::ObserveFocusRings() {
// Create a callback for the focus ring observer.
AccessibilityManager::Get()->SetFocusRingObserverForTest(base::BindRepeating(
&AutoclickTestUtils::OnFocusRingChanged, GetWeakPtr()));
}
void AutoclickTestUtils::WaitForFocusRingChanged() {
loop_runner_ = std::make_unique<base::RunLoop>();
loop_runner_->Run();
}
gfx::Rect AutoclickTestUtils::GetNodeBoundsInRoot(const std::string& name,
const std::string& role) {
return automation_utils_->GetNodeBoundsInRoot(name, role);
}
gfx::Rect AutoclickTestUtils::GetBoundsForNodeInRootByClassName(
const std::string& class_name) {
return automation_utils_->GetBoundsForNodeInRootByClassName(class_name);
}
void AutoclickTestUtils::WaitForAutoclickReady() {
base::ScopedAllowBlockingForTesting allow_blocking;
std::string script = base::StringPrintf(R"JS(
(async function() {
window.accessibilityCommon.setFeatureLoadCallbackForTest('autoclick',
() => {
chrome.test.sendScriptResult('ready');
});
})();
)JS");
base::Value result =
extensions::browsertest_util::ExecuteScriptInBackgroundPage(
profile_, extension_misc::kAccessibilityCommonExtensionId, script);
ASSERT_EQ("ready", result);
}
void AutoclickTestUtils::OnEventTypePrefChanged() {
if (pref_change_waiter_) {
std::move(pref_change_waiter_).Run();
}
}
void AutoclickTestUtils::OnFocusRingChanged() {
if (loop_runner_ && loop_runner_->running()) {
loop_runner_->Quit();
}
}
base::WeakPtr<AutoclickTestUtils> AutoclickTestUtils::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
} // namespace ash