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

#include <memory>
#include <vector>

#include "ash/accessibility/test_event_recorder.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/accessibility_controller_enums.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/memory/raw_ptr.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/devices/device_data_manager_test_api.h"
#include "ui/events/devices/input_device.h"
#include "ui/events/devices/touchpad_device.h"
#include "ui/events/event_rewriter.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/events/test/event_generator.h"
#include "ui/wm/core/cursor_manager.h"

namespace ash {

namespace {

const int kUsbMouseDeviceId = 20;
const int kBluetoothMouseDeviceId = 25;
const int kInternalTouchpadDeviceId = 30;
const uint16_t kLogitechVID = 0x046d;
const uint16_t kMousePID = 0xb034;

ui::InputDevice GetSampleMouseUsb() {
  return {kUsbMouseDeviceId, ui::INPUT_DEVICE_USB, "SampleMouseUsb"};
}

ui::InputDevice GetSampleMouseBluetooth() {
  return {kBluetoothMouseDeviceId,
          ui::INPUT_DEVICE_BLUETOOTH,
          "SampleMouseBluetooth",
          /* phys= */ "",
          base::FilePath(),
          kLogitechVID,
          kMousePID,
          /* version= */ 0};
}

ui::TouchpadDevice GetSampleTouchpadInternal() {
  return {kInternalTouchpadDeviceId, ui::INPUT_DEVICE_INTERNAL, "touchpad"};
}

void SimulateOnlyInternalTouchpadConnected() {
  ui::DeviceDataManagerTestApi().SetTouchpadDevices(
      {GetSampleTouchpadInternal()});
}

void SimulateExternalMouseConnected() {
  ui::DeviceDataManagerTestApi().SetMouseDevices(
      {GetSampleMouseUsb(), GetSampleMouseBluetooth()});

  SimulateOnlyInternalTouchpadConnected();
}

void SetDisableTouchpadMode(DisableTouchpadMode mode) {
  PrefService* prefs =
      Shell::Get()->session_controller()->GetLastActiveUserPrefService();
  prefs->SetInteger(prefs::kAccessibilityDisableTrackpadMode,
                    static_cast<int>(mode));
}

}  // namespace

class DisableTouchpadEventRewriterTest : public AshTestBase {
 public:
  DisableTouchpadEventRewriterTest() = default;
  DisableTouchpadEventRewriterTest(const DisableTouchpadEventRewriterTest&) =
      delete;
  DisableTouchpadEventRewriterTest& operator=(
      const DisableTouchpadEventRewriterTest&) = delete;
  ~DisableTouchpadEventRewriterTest() override = default;

  void PressAndReleaseShiftKey() {
    generator()->PressKey(ui::VKEY_SHIFT, ui::EF_NONE);
    generator()->ReleaseKey(ui::VKEY_SHIFT, ui::EF_NONE);
  }

  void SetUp() override {
    AshTestBase::SetUp();
    event_rewriter_ = std::make_unique<DisableTouchpadEventRewriter>();
    generator_ = AshTestBase::GetEventGenerator();
    GetContext()->GetHost()->GetEventSource()->AddEventRewriter(
        event_rewriter());
    GetContext()->GetHost()->GetEventSource()->AddEventRewriter(
        &event_recorder_);
    event_rewriter()->SetEnabled(true);
  }

  void TearDown() override {
    GetContext()->GetHost()->GetEventSource()->RemoveEventRewriter(
        &event_recorder_);
    GetContext()->GetHost()->GetEventSource()->RemoveEventRewriter(
        event_rewriter());
    event_rewriter_.reset();
    generator_ = nullptr;
    AshTestBase::TearDown();
  }

  ui::test::EventGenerator* generator() { return generator_; }
  TestEventRecorder* event_recorder() { return &event_recorder_; }
  DisableTouchpadEventRewriter* event_rewriter() {
    return event_rewriter_.get();
  }

 private:
  // Generates ui::Events to simulate user input.
  raw_ptr<ui::test::EventGenerator> generator_ = nullptr;
  // Records events delivered to the next event rewriter after
  // DisableTouchpadEventRewriter.
  TestEventRecorder event_recorder_;
  // The DisableTouchpadEventRewriter instance.
  std::unique_ptr<DisableTouchpadEventRewriter> event_rewriter_;
};

TEST_F(DisableTouchpadEventRewriterTest, KeyboardEventsNotCanceledIfDisabled) {
  event_rewriter()->SetEnabled(false);
  generator()->PressKey(ui::VKEY_A, ui::EF_NONE);
  ASSERT_EQ(1U, event_recorder()->events().size());
  ASSERT_EQ(ui::EventType::kKeyPressed,
            event_recorder()->events().back()->type());
  generator()->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
  ASSERT_EQ(2u, event_recorder()->events().size());
  ASSERT_EQ(ui::EventType::kKeyReleased,
            event_recorder()->events().back()->type());
}

TEST_F(DisableTouchpadEventRewriterTest, MouseButtonsNotCanceledIfDisabled) {
  event_rewriter()->SetEnabled(false);
  generator()->PressLeftButton();
  EXPECT_EQ(1U, event_recorder()->events().size());
  EXPECT_EQ(ui::EventType::kMousePressed,
            event_recorder()->events().back()->type());
  generator()->ReleaseLeftButton();
  EXPECT_EQ(2U, event_recorder()->events().size());
  EXPECT_EQ(ui::EventType::kMouseReleased,
            event_recorder()->events().back()->type());
}

TEST_F(DisableTouchpadEventRewriterTest, KeyboardEventsNotCanceled) {
  generator()->PressKey(ui::VKEY_A, ui::EF_NONE);
  ASSERT_EQ(1U, event_recorder()->events().size());
  ASSERT_EQ(ui::EventType::kKeyPressed,
            event_recorder()->events().back()->type());
  generator()->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
  ASSERT_EQ(2u, event_recorder()->events().size());
  ASSERT_EQ(ui::EventType::kKeyReleased,
            event_recorder()->events().back()->type());
}

TEST_F(DisableTouchpadEventRewriterTest, MouseButtonsCanceledInAlwaysMode) {
  event_rewriter()->SetEnabled(true);
  SetDisableTouchpadMode(DisableTouchpadMode::kAlways);
  generator()->set_mouse_source_device_id(kInternalTouchpadDeviceId);
  SimulateOnlyInternalTouchpadConnected();

  generator()->PressLeftButton();
  EXPECT_FALSE(Shell::Get()->cursor_manager()->IsCursorVisible());
  EXPECT_EQ(0U, event_recorder()->events().size());
  generator()->ReleaseLeftButton();
  EXPECT_EQ(0U, event_recorder()->events().size());
}

TEST_F(DisableTouchpadEventRewriterTest, DisableAfterFiveShiftKeyPresses) {
  event_rewriter()->SetEnabled(true);

  int shiftKeyPressCount = 0;

  // Simulate pressing and releasing the shift key 5 times.
  for (int i = 0; i < 5; ++i) {
    PressAndReleaseShiftKey();
    generator()->AdvanceClock(base::Milliseconds(100));
    ++shiftKeyPressCount;

    // After the 5th press, check if the rewriter is disabled.
    if (shiftKeyPressCount == 5) {
      EXPECT_FALSE(event_rewriter()->IsEnabled());
    } else {
      EXPECT_TRUE(event_rewriter()->IsEnabled());
    }
  }
}

TEST_F(DisableTouchpadEventRewriterTest,
       ShiftPressesExceedTimeWindowStaysEnabled) {
  event_rewriter()->SetEnabled(true);

  // Simulate pressing and releasing the shift key 4 times.
  for (int i = 0; i < 4; ++i) {
    PressAndReleaseShiftKey();
    generator()->AdvanceClock(base::Milliseconds(100));
    EXPECT_TRUE(event_rewriter()->IsEnabled());
  }

  // Exceed shift key time window on final key press.
  generator()->AdvanceClock(base::Seconds(3));
  PressAndReleaseShiftKey();
  EXPECT_TRUE(event_rewriter()->IsEnabled());
}

TEST_F(DisableTouchpadEventRewriterTest, ResetShiftKeyCountOnNonShiftKeyPress) {
  event_rewriter()->SetEnabled(true);

  // Simulate pressing shift key and releasing the shift key 3 times.
  for (int i = 0; i < 3; ++i) {
    PressAndReleaseShiftKey();
    EXPECT_TRUE(event_rewriter()->IsEnabled());
  }

  // Press non shift key resets the shift key  count.
  generator()->PressKey(ui::VKEY_A, ui::EF_NONE);
  generator()->ReleaseKey(ui::VKEY_A, ui::EF_NONE);

  EXPECT_TRUE(event_rewriter()->IsEnabled());

  int shiftKeyPressCount = 0;

  for (int i = 0; i < 5; ++i) {
    PressAndReleaseShiftKey();
    generator()->AdvanceClock(base::Milliseconds(100));
    ++shiftKeyPressCount;

    if (shiftKeyPressCount == 5) {
      EXPECT_FALSE(event_rewriter()->IsEnabled());
    } else {
      EXPECT_TRUE(event_rewriter()->IsEnabled());
    }
  }
}

TEST_F(DisableTouchpadEventRewriterTest,
       InternalMouseCanceledWithExternalMouse) {
  event_rewriter()->SetEnabled(true);
  SetDisableTouchpadMode(DisableTouchpadMode::kOnExternalMouseConnected);
  generator()->set_mouse_source_device_id(kInternalTouchpadDeviceId);

  SimulateOnlyInternalTouchpadConnected();
  generator()->PressLeftButton();
  EXPECT_TRUE(Shell::Get()->cursor_manager()->IsCursorVisible());
  EXPECT_EQ(1U, event_recorder()->events().size());
  generator()->ReleaseLeftButton();
  EXPECT_EQ(2U, event_recorder()->events().size());

  SimulateExternalMouseConnected();
  generator()->PressLeftButton();
  EXPECT_FALSE(Shell::Get()->cursor_manager()->IsCursorVisible());
  EXPECT_EQ(2U, event_recorder()->events().size());
  generator()->ReleaseLeftButton();
  EXPECT_EQ(2U, event_recorder()->events().size());
}

TEST_F(DisableTouchpadEventRewriterTest, ExternalMouseAllowedWhenConnected) {
  event_rewriter()->SetEnabled(true);
  SetDisableTouchpadMode(DisableTouchpadMode::kOnExternalMouseConnected);
  generator()->set_mouse_source_device_id(kInternalTouchpadDeviceId);

  SimulateOnlyInternalTouchpadConnected();
  EXPECT_TRUE(Shell::Get()->cursor_manager()->IsCursorVisible());
  generator()->PressLeftButton();
  EXPECT_EQ(1U, event_recorder()->events().size());
  generator()->ReleaseLeftButton();
  EXPECT_EQ(2U, event_recorder()->events().size());

  SimulateExternalMouseConnected();
  generator()->set_mouse_source_device_id(kUsbMouseDeviceId);
  generator()->PressLeftButton();
  EXPECT_TRUE(Shell::Get()->cursor_manager()->IsCursorVisible());
  EXPECT_EQ(3U, event_recorder()->events().size());
  generator()->ReleaseLeftButton();
  EXPECT_EQ(4U, event_recorder()->events().size());
}

}  // namespace ash