910e62b5创建于 1月15日历史提交
// 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