#ifndef CHROME_BROWSER_ASH_ACCESSIBILITY_ACCESSIBILITY_MANAGER_H_
#define CHROME_BROWSER_ASH_ACCESSIBILITY_ACCESSIBILITY_MANAGER_H_
#include <map>
#include <memory>
#include <set>
#include <string>
#include <string_view>
#include <vector>
#include "ash/public/cpp/window_tree_host_lookup.h"
#include "base/callback_list.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_multi_source_observation.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "chrome/browser/ash/accessibility/chromevox_panel.h"
#include "chrome/browser/extensions/api/braille_display_private/braille_controller.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_observer.h"
#include "chrome/common/extensions/api/accessibility_private.h"
#include "chromeos/ash/components/audio/cras_audio_handler.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/session_manager/core/session_manager.h"
#include "components/session_manager/core/session_manager_observer.h"
#include "components/soda/soda_installer.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/scoped_accessibility_mode.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/browser/extension_system.h"
#include "facegaze_settings_event_handler.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/accessibility/public/mojom/assistive_technology_type.mojom.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
#include "ui/accessibility/ax_enums.mojom-forward.h"
#include "ui/base/ime/ash/input_method_manager.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/devices/input_device_event_observer.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/wm/core/coordinate_conversion.h"
class ApplicationLocaleStorage;
class PrefService;
namespace content {
struct FocusedNodeDetails;
}
namespace gfx {
class Rect;
}
namespace ash {
namespace language_packs {
struct PackResult;
}
class AccessibilityDlcInstaller;
class AccessibilityExtensionLoader;
class Dictation;
class SelectToSpeakEventHandlerDelegateImpl;
enum class SelectToSpeakState;
enum class SelectToSpeakPanelAction;
enum class Sound;
struct AccessibilityFocusRingInfo;
enum class AccessibilityNotificationType {
kManagerShutdown,
kToggleHighContrastMode,
kToggleLargeCursor,
kToggleLiveCaption,
kToggleStickyKeys,
kToggleScreenMagnifier,
kToggleSpokenFeedback,
kToggleSelectToSpeak,
kToggleSwitchAccess,
kToggleVirtualKeyboard,
kToggleMonoAudio,
kToggleCaretHighlight,
kToggleCursorHighlight,
kToggleFocusHighlight,
kToggleDictation,
kToggleDockedMagnifier,
};
struct AccessibilityStatusEventDetails {
AccessibilityStatusEventDetails(
AccessibilityNotificationType notification_type,
bool enabled);
AccessibilityNotificationType notification_type;
bool enabled;
};
struct FaceGazeGestureInfo {
std::string gesture;
int confidence;
};
using AccessibilityStatusCallbackList =
base::RepeatingCallbackList<void(const AccessibilityStatusEventDetails&)>;
using AccessibilityStatusCallback =
AccessibilityStatusCallbackList::CallbackType;
using GetTtsDlcContentsCallback =
base::OnceCallback<void(const std::vector<uint8_t>&,
std::optional<std::string>)>;
using InstallFaceGazeAssetsCallback = base::OnceCallback<void(
std::optional<::extensions::api::accessibility_private::FaceGazeAssets>)>;
using InstallPumpkinCallback = base::OnceCallback<void(
std::optional<::extensions::api::accessibility_private::PumpkinData>)>;
class AccessibilityPanelWidgetObserver;
enum class PlaySoundOption {
kAlways = 0,
kOnlyIfSpokenFeedbackEnabled,
};
class AccessibilityManager
: public session_manager::SessionManagerObserver,
public extensions::api::braille_display_private::BrailleObserver,
public extensions::ExtensionRegistryObserver,
public user_manager::UserManager::UserSessionStateObserver,
public input_method::InputMethodManager::Observer,
public CrasAudioHandler::AudioObserver,
public ProfileObserver,
public speech::SodaInstaller::Observer,
public ui::InputDeviceEventObserver {
public:
AccessibilityManager(const AccessibilityManager&) = delete;
AccessibilityManager& operator=(const AccessibilityManager&) = delete;
static void Initialize(
PrefService* local_state,
const ApplicationLocaleStorage* application_locale_storage);
static void Shutdown();
static AccessibilityManager* Get();
static void ShowAccessibilityHelp();
bool ShouldShowAccessibilityMenu();
void ShowChromeVoxTutorial();
void EnableLargeCursor(bool enabled);
bool IsLargeCursorEnabled() const;
void EnableLiveCaption(bool enabled);
bool IsLiveCaptionEnabled() const;
void EnableStickyKeys(bool enabled);
bool IsStickyKeysEnabled() const;
bool IsTouchpadDisabled() const;
void EnableSpokenFeedback(bool enabled);
void EnableSpokenFeedbackWithTutorial();
bool IsSpokenFeedbackEnabled() const;
void EnableHighContrast(bool enabled);
bool IsHighContrastEnabled() const;
void EnableAutoclick(bool enabled);
bool IsAutoclickEnabled() const;
void EnableReducedAnimations(bool enabled);
bool IsReducedAnimationsEnabled() const;
void EnableAlwaysShowScrollbars(bool enabled);
bool IsAlwaysShowScrollbarsEnabled() const;
void EnableFaceGaze(bool enabled);
bool IsFaceGazeEnabled() const;
void RequestEnableFaceGaze(bool enable);
void SendFaceGazeDisableDialogResultToSettings(bool accepted);
void AddFaceGazeSettingsEventHandler(FaceGazeSettingsEventHandler* handler);
void RemoveFaceGazeSettingsEventHandler();
void ToggleGestureInfoForSettings(bool enabled) const;
void SendGestureInfoToSettings(
const std::vector<FaceGazeGestureInfo>& gesture_info) const;
void RequestAutoclickScrollableBoundsForPoint(
const gfx::Point& point_in_screen);
void MagnifierBoundsChanged(const gfx::Rect& bounds_in_screen);
void EnableVirtualKeyboard(bool enabled);
bool IsVirtualKeyboardEnabled() const;
void EnableMonoAudio(bool enabled);
bool IsMonoAudioEnabled() const;
void SetDarkenScreen(bool darken);
void SetCaretHighlightEnabled(bool enabled);
bool IsCaretHighlightEnabled() const;
void SetCursorHighlightEnabled(bool enabled);
bool IsCursorHighlightEnabled() const;
void SetDictationEnabled(bool enabled) const;
bool IsDictationEnabled() const;
void SetFocusHighlightEnabled(bool enabled);
bool IsFocusHighlightEnabled() const;
void EnableTapDragging(bool enabled);
bool IsTapDraggingEnabled() const;
void SetSelectToSpeakEnabled(bool enabled);
bool IsSelectToSpeakEnabled() const;
void RequestSelectToSpeakStateChange();
void SetSelectToSpeakState(SelectToSpeakState state);
void OnSelectToSpeakContextMenuClick();
void SetSwitchAccessEnabled(bool enabled);
bool IsSwitchAccessEnabled() const;
void SetColorCorrectionEnabled(bool enabled);
bool IsColorCorrectionEnabled() const;
bool IsBrailleDisplayConnected() const;
bool IsFullscreenMagnifierEnabled() const;
bool IsDockedMagnifierEnabled() const;
bool AllowQRCodeUX() const;
void ActiveUserChanged(user_manager::User* active_user) override;
base::TimeDelta PlayShutdownSound();
[[nodiscard]] base::CallbackListSubscription RegisterCallback(
const AccessibilityStatusCallback& cb);
void NotifyAccessibilityStatusChanged(
const AccessibilityStatusEventDetails& details);
void OnLocaleChanged();
bool ShouldToggleSpokenFeedbackViaTouch();
bool PlaySpokenFeedbackToggleCountdown(int tick_count);
void OnViewFocusedInArc(const gfx::Rect& bounds_in_screen);
bool PlayEarcon(Sound sound_key, PlaySoundOption option);
void HandleAccessibilityGesture(ax::mojom::Gesture gesture,
gfx::PointF location);
void SetTouchAccessibilityAnchorPoint(const gfx::Point& anchor_point);
void OnChromeVoxPanelDestroying();
Profile* profile() { return profile_; }
void SetKeyboardListenerExtensionId(const std::string& id,
content::BrowserContext* context);
const std::string& keyboard_listener_extension_id() {
return keyboard_listener_extension_id_;
}
void OnSwitchAccessDisabled();
bool ToggleDictation();
std::string GetDictationDefaultLocale(bool new_user);
void SetFocusRing(std::string focus_ring_id,
std::unique_ptr<AccessibilityFocusRingInfo> focus_ring);
void HideFocusRing(std::string focus_ring_id);
std::set<std::string>& GetFocusRingsForATType(
ax::mojom::AssistiveTechnologyType at_type);
void RemoveFocusRings(ax::mojom::AssistiveTechnologyType at_type);
void SetHighlights(const std::vector<gfx::Rect>& rects_in_screen,
SkColor color);
void HideHighlights();
void SetCaretBounds(const gfx::Rect& bounds_in_screen);
bool GetStartupSoundEnabled() const;
void SetStartupSoundEnabled(bool value) const;
void PreviewFlashNotification() const;
const std::string GetBluetoothBrailleDisplayAddress() const;
void UpdateBluetoothBrailleDisplayAddress(const std::string& address);
void OpenSettingsSubpage(const std::string& subpage);
const std::string GetFocusRingId(ax::mojom::AssistiveTechnologyType at_type,
const std::string& focus_ring_name);
void OnSelectToSpeakPanelAction(SelectToSpeakPanelAction action,
double value);
void SendKeysCurrentlyDownToSelectToSpeak(
const std::set<ui::KeyboardCode>& pressed_keys);
void SendMouseEventToSelectToSpeak(ui::EventType type,
const gfx::PointF& position);
void OnShimlessRmaLaunched();
void SendSyntheticMouseEvent(ui::EventType type,
int flags,
int changed_button_flags,
gfx::Point location_in_screen,
bool use_rewriters);
std::optional<ui::KeyboardCode> GetCaretBrowsingActionKey();
void OnSodaInstalled(speech::LanguageCode language_code) override;
void OnSodaInstallError(speech::LanguageCode language_code,
speech::SodaInstaller::ErrorCode error_code) override;
void OnSodaProgress(speech::LanguageCode language_code,
int progress) override;
void OnInputDeviceConfigurationChanged(uint8_t input_device_type) override;
void OnDeviceListsComplete() override;
void SetProfileForTest(Profile* profile);
static void SetBrailleControllerForTest(
extensions::api::braille_display_private::BrailleController* controller);
void SetScreenDarkenObserverForTest(base::RepeatingCallback<void()> observer);
void SetOpenSettingsSubpageObserverForTest(
base::RepeatingCallback<void()> observer);
void SetFocusRingObserverForTest(base::RepeatingCallback<void()> observer);
void SetHighlightsObserverForTest(base::RepeatingCallback<void()> observer);
void SetSelectToSpeakStateObserverForTest(
base::RepeatingCallback<void()> observer);
void SetCaretBoundsObserverForTest(
base::RepeatingCallback<void(const gfx::Rect&)> observer);
void SetMagnifierBoundsObserverForTest(
base::RepeatingCallback<void()> observer);
void SetSwitchAccessKeysForTest(const std::set<int>& action_keys,
const std::string& pref_name);
const std::set<std::string>& GetAccessibilityCommonEnabledFeaturesForTest() {
return accessibility_common_enabled_features_;
}
bool IsDisableAutoclickDialogVisibleForTest();
bool is_pumpkin_installed_for_testing() {
return is_pumpkin_installed_for_testing_;
}
void InstallFaceGazeAssets(InstallFaceGazeAssetsCallback callback);
void InstallPumpkinForDictation(InstallPumpkinCallback callback);
void GetTtsDlcContents(
::extensions::api::accessibility_private::DlcType dlc,
::extensions::api::accessibility_private::TtsVariant variant,
GetTtsDlcContentsCallback callback);
void GetTtsDlcContentsOnPackState(
::extensions::api::accessibility_private::TtsVariant variant,
GetTtsDlcContentsCallback callback,
const language_packs::PackResult& pack_result);
void SetDlcPathForTest(base::FilePath path);
void LoadEnhancedNetworkTtsForTest();
protected:
AccessibilityManager(
PrefService* local_state,
const ApplicationLocaleStorage* application_locale_storage);
~AccessibilityManager() override;
private:
void PostLoadChromeVox();
void PostUnloadChromeVox();
void PostSwitchChromeVoxProfile();
void PostLoadSelectToSpeak();
void PostUnloadSelectToSpeak();
void PostUnloadSwitchAccess();
void PostUnloadAccessibilityCommon();
void UpdateEnhancedNetworkTts();
void LoadEnhancedNetworkTts();
void UnloadEnhancedNetworkTts();
void PostLoadEnhancedNetworkTts();
void UpdateAlwaysShowMenuFromPref();
void OnFaceGazeChanged();
void OnLargeCursorChanged();
void OnLiveCaptionChanged();
void OnStickyKeysChanged();
void OnSpokenFeedbackChanged();
void OnHighContrastChanged();
void OnVirtualKeyboardChanged();
void OnMonoAudioChanged();
void OnCaretHighlightChanged();
void OnCursorHighlightChanged();
void OnFocusHighlightChanged();
void OnTapDraggingChanged();
void OnSelectToSpeakChanged();
void OnAccessibilityCommonChanged(const std::string& pref_name);
void OnSwitchAccessChanged();
void OnReducedAnimationsChanged() const;
void OnFocusChangedInPage(const content::FocusedNodeDetails& details);
void OnDictationChanged(bool triggered_by_user);
void OnDictationLocaleChanged();
void MaybeShowNetworkDictationDialogOrInstallSoda(
const std::string& dictation_locale);
void CheckBrailleState();
void ReceiveBrailleDisplayState(
std::unique_ptr<extensions::api::braille_display_private::DisplayState>
state);
void UpdateBrailleImeState();
void SetProfile(Profile* profile);
void SetProfileByUser(const user_manager::User* user);
void UpdateChromeOSAccessibilityHistograms();
void PlayVolumeAdjustSound();
void OnLoginOrLockScreenVisible() override;
void OnSessionStateChanged() override;
void SetActiveProfile();
void OnBrailleDisplayStateChanged(
const extensions::api::braille_display_private::DisplayState&
display_state) override;
void OnBrailleKeyEvent(
const extensions::api::braille_display_private::KeyEvent& event) override;
void OnExtensionUnloaded(content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UnloadedExtensionReason reason) override;
void OnShutdown(extensions::ExtensionRegistry* registry) override;
void InputMethodChanged(input_method::InputMethodManager* manager,
Profile* profile,
bool show_message) override;
void OnActiveOutputNodeChanged() override;
void OnProfileWillBeDestroyed(Profile* profile) override;
bool ShouldShowNetworkDictationDialog(const std::string& locale);
void ShowNetworkDictationDialog();
void OnNetworkDictationDialogAccepted();
void OnNetworkDictationDialogDismissed();
void MaybeInstallSoda(const std::string& locale);
void OnSodaInstallUpdated(int progress);
bool ShouldShowSodaSucceededNotificationForDictation();
bool ShouldShowSodaFailedNotificationForDictation(
speech::LanguageCode language_code);
void UpdateDictationNotification();
void ShowDictationLanguageUpgradedNudge(const std::string& locale);
speech::LanguageCode GetDictationLanguageCode();
void CreateChromeVoxPanel();
void OnFaceGazeAssetsInstalled(bool success, const std::string& root_path);
void OnFaceGazeAssetsFailed(std::string_view error);
void OnFaceGazeAssetsCreated(
std::optional<::extensions::api::accessibility_private::FaceGazeAssets>
assets);
void OnPumpkinInstalled(bool success, const std::string& root_path);
void OnPumpkinError(std::string_view error);
void OnPumpkinDataCreated(
std::optional<::extensions::api::accessibility_private::PumpkinData>
data);
void OnAppTerminating();
void MaybeLogBrailleDisplayConnectedTime();
bool spoken_feedback_enabled() const { return bool(screen_reader_mode_); }
const raw_ref<PrefService> local_state_;
const raw_ref<const ApplicationLocaleStorage> application_locale_storage_;
raw_ptr<Profile> profile_ = nullptr;
base::ScopedObservation<Profile, ProfileObserver> profile_observation_{this};
base::ScopedObservation<session_manager::SessionManager,
session_manager::SessionManagerObserver>
session_observation_{this};
std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
std::unique_ptr<PrefChangeRegistrar> local_state_pref_change_registrar_;
std::unique_ptr<content::ScopedAccessibilityMode> screen_reader_mode_;
bool select_to_speak_enabled_ = false;
bool switch_access_enabled_ = false;
bool start_chromevox_with_tutorial_ = false;
std::set<std::string> accessibility_common_enabled_features_;
AccessibilityStatusCallbackList callback_list_;
bool braille_display_connected_ = false;
base::Time braille_display_connect_time_;
base::ScopedObservation<
extensions::api::braille_display_private::BrailleController,
extensions::api::braille_display_private::BrailleObserver>
scoped_braille_observation_{this};
base::ScopedObservation<speech::SodaInstaller,
speech::SodaInstaller::Observer>
soda_observation_{this};
bool braille_ime_current_ = false;
raw_ptr<FaceGazeSettingsEventHandler> facegaze_settings_event_handler_;
raw_ptr<ChromeVoxPanel> chromevox_panel_ = nullptr;
std::unique_ptr<AccessibilityPanelWidgetObserver>
chromevox_panel_widget_observer_;
std::string keyboard_listener_extension_id_;
bool keyboard_listener_capture_ = false;
base::ScopedMultiSourceObservation<extensions::ExtensionRegistry,
extensions::ExtensionRegistryObserver>
extension_registry_observations_{this};
std::unique_ptr<AccessibilityExtensionLoader>
accessibility_common_extension_loader_;
std::unique_ptr<AccessibilityExtensionLoader> chromevox_loader_;
std::unique_ptr<AccessibilityExtensionLoader> select_to_speak_loader_;
std::unique_ptr<SelectToSpeakEventHandlerDelegateImpl>
select_to_speak_event_handler_delegate_;
std::unique_ptr<AccessibilityExtensionLoader> switch_access_loader_;
std::unique_ptr<AccessibilityDlcInstaller> dlc_installer_;
std::map<ax::mojom::AssistiveTechnologyType, std::set<std::string>>
focus_ring_names_for_at_type_;
bool app_terminating_ = false;
bool dictation_active_ = false;
bool network_dictation_dialog_is_showing_ = false;
bool soda_failed_notification_shown_ = false;
bool dictation_triggered_by_user_ = false;
bool ignore_dictation_locale_pref_change_ = false;
base::RepeatingCallback<void()> screen_darken_observer_for_test_;
base::RepeatingCallback<void()> open_settings_subpage_observer_for_test_;
base::RepeatingCallback<void()> highlights_observer_for_test_;
base::RepeatingCallback<void()> select_to_speak_state_observer_for_test_;
base::RepeatingCallback<void(const gfx::Rect&)>
caret_bounds_observer_for_test_;
base::RepeatingCallback<void()> magnifier_bounds_observer_for_test_;
base::OnceClosure enhanced_network_tts_waiter_for_test_;
mojo::Remote<media_session::mojom::AudioFocusManager> audio_focus_manager_;
bool was_vk_enabled_before_switch_access_ = false;
bool locked_ = false;
InstallFaceGazeAssetsCallback install_facegaze_assets_callback_;
InstallPumpkinCallback install_pumpkin_callback_;
bool is_pumpkin_installed_for_testing_ = false;
base::FilePath dlc_path_for_test_;
base::CallbackListSubscription focus_changed_subscription_;
base::CallbackListSubscription on_app_terminating_subscription_;
base::WeakPtrFactory<AccessibilityManager> weak_ptr_factory_{this};
friend class AccessibilityManagerDictationDialogTest;
friend class AccessibilityManagerDictationKeyboardImprovementsTest;
friend class AccessibilityManagerDlcTest;
friend class AccessibilityManagerNoOnDeviceSpeechRecognitionTest;
friend class AccessibilityManagerTest;
friend class DictationTest;
friend class SwitchAccessTest;
};
}
#endif