blob: 5cffb88f9a5d0f5066ce34b03b515d45f136a2ae [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
#include "base/memory/raw_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "content/public/browser/scoped_accessibility_mode.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/scoped_accessibility_mode_override.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/accessibility/platform/ax_mode_observer.h"
#include "ui/accessibility/platform/ax_platform.h"
#include "ui/accessibility/platform/browser_accessibility_manager.h"
#include "ui/accessibility/platform/test_ax_node_id_delegate.h"
#include "ui/accessibility/platform/test_ax_platform_tree_manager_delegate.h"
#include "ui/events/base_event_utils.h"
#if BUILDFLAG(IS_ANDROID)
#include "content/browser/accessibility/browser_accessibility_manager_android.h"
#endif
namespace content {
class BrowserAccessibilityStateImplTest : public ::testing::Test {
public:
BrowserAccessibilityStateImplTest() = default;
BrowserAccessibilityStateImplTest(const BrowserAccessibilityStateImplTest&) =
delete;
~BrowserAccessibilityStateImplTest() override = default;
protected:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(
features::kAutoDisableAccessibility);
// Set the initial time to something non-zero.
task_environment_.FastForwardBy(base::Seconds(100));
state_ = BrowserAccessibilityStateImpl::GetInstance();
}
base::test::ScopedFeatureList scoped_feature_list_;
raw_ptr<BrowserAccessibilityStateImpl> state_;
BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
std::unique_ptr<ui::TestAXPlatformTreeManagerDelegate>
test_browser_accessibility_delegate_;
ui::TestAXNodeIdDelegate node_id_delegate_;
};
TEST_F(BrowserAccessibilityStateImplTest,
AddAccessibilityModeFlagsPreventsAutoDisableAccessibility) {
// Initially, accessibility should be disabled.
EXPECT_EQ(ui::AXPlatform::GetInstance().GetMode(), ui::AXMode());
// Enable accessibility.
ScopedAccessibilityModeOverride scoped_mode(ui::kAXModeComplete);
EXPECT_EQ(ui::AXPlatform::GetInstance().GetMode(), ui::kAXModeComplete);
// Send user input, wait 31 seconds, then send another user input event -
// but add a new accessibility mode flag.
state_->OnUserInputEvent();
state_->OnUserInputEvent();
task_environment_.FastForwardBy(base::Seconds(31));
ScopedAccessibilityModeOverride scoped_mode_2(ui::kAXModeComplete);
state_->OnUserInputEvent();
// Accessibility should still be enabled.
EXPECT_EQ(ui::AXPlatform::GetInstance().GetMode(), ui::kAXModeComplete);
}
TEST_F(BrowserAccessibilityStateImplTest,
GetRolePreventsAutoDisableAccessibility) {
// Create a bare-minimum accessibility tree so we can call GetRole().
ui::AXNodeData root;
root.id = 1;
root.role = ax::mojom::Role::kRootWebArea;
ui::BrowserAccessibilityManager* manager;
#if BUILDFLAG(IS_ANDROID)
manager = BrowserAccessibilityManagerAndroid::Create(
MakeAXTreeUpdateForTesting(root), node_id_delegate_,
test_browser_accessibility_delegate_.get());
#else
manager = ui::BrowserAccessibilityManager::Create(
MakeAXTreeUpdateForTesting(root), node_id_delegate_,
test_browser_accessibility_delegate_.get());
#endif
std::unique_ptr<ui::BrowserAccessibilityManager>
browser_accessibility_manager(manager);
ui::BrowserAccessibility* ax_root =
browser_accessibility_manager->GetBrowserAccessibilityRoot();
ASSERT_NE(nullptr, ax_root);
// Initially, accessibility should be disabled.
EXPECT_EQ(ui::AXPlatform::GetInstance().GetMode(), ui::AXMode());
// Enable accessibility.
ScopedAccessibilityModeOverride scoped_mode(ui::kAXModeComplete);
EXPECT_EQ(ui::AXPlatform::GetInstance().GetMode(), ui::kAXModeComplete);
// Send user input, wait 31 seconds, then send another user input event after
// checking the role, which should register accessibility API usage.
state_->OnUserInputEvent();
state_->OnUserInputEvent();
task_environment_.FastForwardBy(base::Seconds(31));
ax_root->GetRole();
state_->OnUserInputEvent();
// Accessibility should still be enabled due to GetRole() being called.
EXPECT_EQ(ui::AXPlatform::GetInstance().GetMode(), ui::kAXModeComplete);
}
namespace {
using ::testing::_;
class MockAXModeObserver : public ui::AXModeObserver {
public:
MOCK_METHOD(void, OnAXModeAdded, (ui::AXMode mode), (override));
};
} // namespace
TEST_F(BrowserAccessibilityStateImplTest,
EnablingAccessibilityTwiceSendsASingleNotification) {
// Initially accessibility should be disabled.
EXPECT_EQ(ui::AXPlatform::GetInstance().GetMode(), ui::AXMode());
auto& ax_platform = ui::AXPlatform::GetInstance();
::testing::StrictMock<MockAXModeObserver> mock_observer;
base::ScopedObservation<ui::AXPlatform, ui::AXModeObserver>
scoped_observation(&mock_observer);
scoped_observation.Observe(&ax_platform);
// Enable accessibility.
EXPECT_CALL(mock_observer, OnAXModeAdded(ui::kAXModeComplete));
ScopedAccessibilityModeOverride scoped_mode(ui::kAXModeComplete);
::testing::Mock::VerifyAndClearExpectations(&mock_observer);
// A second call should be a no-op.
ScopedAccessibilityModeOverride scoped_mode_2(ui::kAXModeComplete);
::testing::Mock::VerifyAndClearExpectations(&mock_observer);
}
// Tests platform activation filtering.
TEST_F(BrowserAccessibilityStateImplTest, PlatformActivationFiltering) {
// Disabled by default in all unit tests.
ASSERT_FALSE(state_->IsActivationFromPlatformEnabled());
ASSERT_EQ(state_->GetAccessibilityMode(), ui::AXMode());
{
// Adding a modes from the platform is a no-op.
auto complete = state_->CreateScopedModeForProcess(
ui::kAXModeComplete | ui::AXMode::kFromPlatform);
ASSERT_EQ(state_->GetAccessibilityMode(), ui::AXMode());
// Until platform activation is enabled.
state_->SetActivationFromPlatformEnabled(true);
ASSERT_TRUE(state_->IsActivationFromPlatformEnabled());
EXPECT_EQ(state_->GetAccessibilityMode(), ui::kAXModeComplete);
// Enabling when already enabled does nothing.
state_->SetActivationFromPlatformEnabled(true);
ASSERT_TRUE(state_->IsActivationFromPlatformEnabled());
EXPECT_EQ(state_->GetAccessibilityMode(), ui::kAXModeComplete);
state_->SetActivationFromPlatformEnabled(false);
}
{
// Adding modes without the bit works as expected.
auto basic = state_->CreateScopedModeForProcess(ui::kAXModeBasic);
EXPECT_EQ(state_->GetAccessibilityMode() & ui::kAXModeBasic,
ui::kAXModeBasic);
// And filtering has no impact.
state_->SetActivationFromPlatformEnabled(true);
EXPECT_EQ(state_->GetAccessibilityMode() & ui::kAXModeBasic,
ui::kAXModeBasic);
state_->SetActivationFromPlatformEnabled(false);
}
}
} // namespace content