1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ash/accessibility/accessibility_controller_impl.h"
6 
7 #include <map>
8 #include <memory>
9 #include <set>
10 #include <utility>
11 
12 #include "ash/accelerators/accelerator_controller_impl.h"
13 #include "ash/accessibility/accessibility_highlight_controller.h"
14 #include "ash/accessibility/accessibility_observer.h"
15 #include "ash/accessibility/accessibility_panel_layout_manager.h"
16 #include "ash/accessibility/point_scan_controller.h"
17 #include "ash/autoclick/autoclick_controller.h"
18 #include "ash/events/accessibility_event_rewriter.h"
19 #include "ash/events/select_to_speak_event_handler.h"
20 #include "ash/high_contrast/high_contrast_controller.h"
21 #include "ash/keyboard/keyboard_controller_impl.h"
22 #include "ash/keyboard/ui/keyboard_util.h"
23 #include "ash/login_status.h"
24 #include "ash/policy/policy_recommendation_restorer.h"
25 #include "ash/public/cpp/accessibility_controller_client.h"
26 #include "ash/public/cpp/ash_constants.h"
27 #include "ash/public/cpp/ash_pref_names.h"
28 #include "ash/public/cpp/notification_utils.h"
29 #include "ash/public/cpp/session/session_observer.h"
30 #include "ash/public/cpp/shell_window_ids.h"
31 #include "ash/resources/vector_icons/vector_icons.h"
32 #include "ash/session/session_controller_impl.h"
33 #include "ash/shell.h"
34 #include "ash/sticky_keys/sticky_keys_controller.h"
35 #include "ash/strings/grit/ash_strings.h"
36 #include "ash/system/accessibility/accessibility_feature_disable_dialog.h"
37 #include "ash/system/accessibility/floating_accessibility_controller.h"
38 #include "ash/system/accessibility/select_to_speak_menu_bubble_controller.h"
39 #include "ash/system/accessibility/switch_access_menu_bubble_controller.h"
40 #include "ash/system/power/backlights_forced_off_setter.h"
41 #include "ash/system/power/power_status.h"
42 #include "ash/system/power/scoped_backlights_forced_off.h"
43 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
44 #include "base/bind.h"
45 #include "base/callback_helpers.h"
46 #include "base/metrics/histogram_functions.h"
47 #include "base/metrics/user_metrics.h"
48 #include "base/strings/string16.h"
49 #include "chromeos/audio/cras_audio_handler.h"
50 #include "components/pref_registry/pref_registry_syncable.h"
51 #include "components/prefs/pref_change_registrar.h"
52 #include "components/prefs/pref_registry_simple.h"
53 #include "components/prefs/pref_service.h"
54 #include "ui/accessibility/accessibility_features.h"
55 #include "ui/accessibility/accessibility_switches.h"
56 #include "ui/aura/window.h"
57 #include "ui/base/cursor/cursor_size.h"
58 #include "ui/base/l10n/l10n_util.h"
59 #include "ui/message_center/message_center.h"
60 #include "ui/message_center/public/cpp/notifier_id.h"
61 #include "ui/wm/core/cursor_manager.h"
62 
63 using session_manager::SessionState;
64 
65 namespace ash {
66 namespace {
67 
68 using FeatureType = AccessibilityControllerImpl::FeatureType;
69 
70 // These classes are used to store the static configuration for a11y features.
71 struct FeatureData {
72   FeatureType type;
73   const char* pref;
74   const gfx::VectorIcon* icon;
75   FeatureType conflicting_feature = FeatureType::kNoConflictingFeature;
76 };
77 
78 struct FeatureDialogData {
79   AccessibilityControllerImpl::FeatureType type;
80   const char* pref;
81   int title;
82   int body;
83   bool mandatory;
84 };
85 
86 // A static array describing each feature.
87 const FeatureData kFeatures[] = {
88     {FeatureType::kAutoclick, prefs::kAccessibilityAutoclickEnabled,
89      &kSystemMenuAccessibilityAutoClickIcon},
90     {FeatureType::kCaretHighlight, prefs::kAccessibilityCaretHighlightEnabled,
91      nullptr},
92     {FeatureType::KCursorHighlight, prefs::kAccessibilityCursorHighlightEnabled,
93      nullptr},
94     {FeatureType::kCursorColor, prefs::kAccessibilityCursorColorEnabled,
95      nullptr},
96     {FeatureType::kDictation, prefs::kAccessibilityDictationEnabled,
97      &kDictationMenuIcon},
98     {FeatureType::kFocusHighlight, prefs::kAccessibilityFocusHighlightEnabled,
99      nullptr, /* conflicting_feature= */ FeatureType::kSpokenFeedback},
100     {FeatureType::kFloatingMenu, prefs::kAccessibilityFloatingMenuEnabled,
101      nullptr},
102     {FeatureType::kFullscreenMagnifier,
103      prefs::kAccessibilityScreenMagnifierEnabled,
104      &kSystemMenuAccessibilityFullscreenMagnifierIcon},
105     {FeatureType::kDockedMagnifier, prefs::kDockedMagnifierEnabled,
106      &kSystemMenuAccessibilityDockedMagnifierIcon},
107     {FeatureType::kHighContrast, prefs::kAccessibilityHighContrastEnabled,
108      &kSystemMenuAccessibilityContrastIcon},
109     {FeatureType::kLargeCursor, prefs::kAccessibilityLargeCursorEnabled,
110      nullptr},
111     {FeatureType::kMonoAudio, prefs::kAccessibilityMonoAudioEnabled, nullptr},
112     {FeatureType::kSpokenFeedback, prefs::kAccessibilitySpokenFeedbackEnabled,
113      &kSystemMenuAccessibilityChromevoxIcon},
114     {FeatureType::kSelectToSpeak, prefs::kAccessibilitySelectToSpeakEnabled,
115      &kSystemMenuAccessibilitySelectToSpeakIcon},
116     {FeatureType::kStickyKeys, prefs::kAccessibilityStickyKeysEnabled, nullptr},
117     {FeatureType::kSwitchAccess, prefs::kAccessibilitySwitchAccessEnabled,
118      &kSwitchAccessIcon},
119     {FeatureType::kVirtualKeyboard, prefs::kAccessibilityVirtualKeyboardEnabled,
120      &kSystemMenuKeyboardIcon}};
121 
122 // An array describing the confirmation dialogs for the features which have
123 // them.
124 const FeatureDialogData kFeatureDialogs[] = {
125     {FeatureType::kDictation, prefs::kDictationAcceleratorDialogHasBeenAccepted,
126      IDS_ASH_DICTATION_CONFIRMATION_TITLE, IDS_ASH_DICTATION_CONFIRMATION_BODY,
127      true},
128     {FeatureType::kFullscreenMagnifier,
129      prefs::kScreenMagnifierAcceleratorDialogHasBeenAccepted,
130      IDS_ASH_SCREEN_MAGNIFIER_TITLE, IDS_ASH_SCREEN_MAGNIFIER_BODY, false},
131     {FeatureType::kDockedMagnifier,
132      prefs::kDockedMagnifierAcceleratorDialogHasBeenAccepted,
133      IDS_ASH_DOCKED_MAGNIFIER_TITLE, IDS_ASH_DOCKED_MAGNIFIER_BODY, false},
134     {FeatureType::kHighContrast,
135      prefs::kHighContrastAcceleratorDialogHasBeenAccepted,
136      IDS_ASH_HIGH_CONTRAST_TITLE, IDS_ASH_HIGH_CONTRAST_BODY, false}};
137 
138 constexpr char kNotificationId[] = "chrome://settings/accessibility";
139 constexpr char kNotifierAccessibility[] = "ash.accessibility";
140 
141 // TODO(warx): Signin screen has more controllable accessibility prefs. We may
142 // want to expand this to a complete list. If so, merge this with
143 // |kCopiedOnSigninAccessibilityPrefs|.
144 constexpr const char* const kA11yPrefsForRecommendedValueOnSignin[]{
145     prefs::kAccessibilityLargeCursorEnabled,
146     prefs::kAccessibilityHighContrastEnabled,
147     prefs::kAccessibilityScreenMagnifierEnabled,
148     prefs::kAccessibilitySpokenFeedbackEnabled,
149     prefs::kAccessibilityVirtualKeyboardEnabled,
150 };
151 
152 // List of accessibility prefs that are to be copied (if changed by the user) on
153 // signin screen profile to a newly created user profile or a guest session.
154 constexpr const char* const kCopiedOnSigninAccessibilityPrefs[]{
155     prefs::kAccessibilityAutoclickDelayMs,
156     prefs::kAccessibilityAutoclickEnabled,
157     prefs::kAccessibilityCaretHighlightEnabled,
158     prefs::kAccessibilityCursorHighlightEnabled,
159     prefs::kAccessibilityCursorColorEnabled,
160     prefs::kAccessibilityCursorColor,
161     prefs::kAccessibilityDictationEnabled,
162     prefs::kAccessibilityFocusHighlightEnabled,
163     prefs::kAccessibilityHighContrastEnabled,
164     prefs::kAccessibilityLargeCursorEnabled,
165     prefs::kAccessibilityMonoAudioEnabled,
166     prefs::kAccessibilityScreenMagnifierEnabled,
167     prefs::kAccessibilityScreenMagnifierFocusFollowingEnabled,
168     prefs::kAccessibilityScreenMagnifierScale,
169     prefs::kAccessibilitySelectToSpeakEnabled,
170     prefs::kAccessibilitySpokenFeedbackEnabled,
171     prefs::kAccessibilityStickyKeysEnabled,
172     prefs::kAccessibilityShortcutsEnabled,
173     prefs::kAccessibilitySwitchAccessEnabled,
174     prefs::kAccessibilityVirtualKeyboardEnabled,
175     prefs::kDockedMagnifierEnabled,
176     prefs::kDockedMagnifierScale,
177     prefs::kHighContrastAcceleratorDialogHasBeenAccepted,
178     prefs::kScreenMagnifierAcceleratorDialogHasBeenAccepted,
179     prefs::kDockedMagnifierAcceleratorDialogHasBeenAccepted,
180     prefs::kDictationAcceleratorDialogHasBeenAccepted,
181     prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted2,
182 };
183 
184 // List of switch access accessibility prefs that are to be copied (if changed
185 // by the user) from the current user to the signin screen profile. That way
186 // if a switch access user signs out, their switch continues to function.
187 constexpr const char* const kSwitchAccessPrefsCopiedToSignin[]{
188     prefs::kAccessibilitySwitchAccessAutoScanEnabled,
189     prefs::kAccessibilitySwitchAccessAutoScanKeyboardSpeedMs,
190     prefs::kAccessibilitySwitchAccessAutoScanSpeedMs,
191     prefs::kAccessibilitySwitchAccessEnabled,
192     prefs::kAccessibilitySwitchAccessNextKeyCodes,
193     prefs::kAccessibilitySwitchAccessNextSetting,
194     prefs::kAccessibilitySwitchAccessPreviousKeyCodes,
195     prefs::kAccessibilitySwitchAccessPreviousSetting,
196     prefs::kAccessibilitySwitchAccessSelectKeyCodes,
197     prefs::kAccessibilitySwitchAccessSelectSetting,
198 };
199 
200 // Helper function that is used to verify the validity of kFeatures and
201 // kFeatureDialogs.
VerifyFeaturesData()202 bool VerifyFeaturesData() {
203   // All feature prefs must be unique.
204   std::set<const char*> feature_prefs;
205   for (auto feature_data : kFeatures) {
206     if (feature_prefs.find(feature_data.pref) != feature_prefs.end())
207       return false;
208     feature_prefs.insert(feature_data.pref);
209   }
210 
211   for (auto dialog_data : kFeatureDialogs) {
212     if (feature_prefs.find(dialog_data.pref) != feature_prefs.end())
213       return false;
214     feature_prefs.insert(dialog_data.pref);
215   }
216 
217   return true;
218 }
219 
220 // Returns true if |pref_service| is the one used for the signin screen.
IsSigninPrefService(PrefService * pref_service)221 bool IsSigninPrefService(PrefService* pref_service) {
222   const PrefService* signin_pref_service =
223       Shell::Get()->session_controller()->GetSigninScreenPrefService();
224   DCHECK(signin_pref_service);
225   return pref_service == signin_pref_service;
226 }
227 
228 // Returns true if the current session is the guest session.
IsCurrentSessionGuest()229 bool IsCurrentSessionGuest() {
230   const base::Optional<user_manager::UserType> user_type =
231       Shell::Get()->session_controller()->GetUserType();
232   return user_type && *user_type == user_manager::USER_TYPE_GUEST;
233 }
234 
IsUserFirstLogin()235 bool IsUserFirstLogin() {
236   return Shell::Get()->session_controller()->IsUserFirstLogin();
237 }
238 
239 // The copying of any modified accessibility prefs on the signin prefs happens
240 // when the |previous_pref_service| is of the signin profile, and the
241 // |current_pref_service| is of a newly created profile first logged in, or if
242 // the current session is the guest session.
ShouldCopySigninPrefs(PrefService * previous_pref_service,PrefService * current_pref_service)243 bool ShouldCopySigninPrefs(PrefService* previous_pref_service,
244                            PrefService* current_pref_service) {
245   DCHECK(previous_pref_service);
246   if (IsUserFirstLogin() && IsSigninPrefService(previous_pref_service) &&
247       !IsSigninPrefService(current_pref_service)) {
248     // If the user set a pref value on the login screen and is now starting a
249     // session with a new profile, copy the pref value to the profile.
250     return true;
251   }
252 
253   if (IsCurrentSessionGuest()) {
254     // Guest sessions don't have their own prefs, so always copy.
255     return true;
256   }
257 
258   return false;
259 }
260 
261 // On a user's first login into a device, any a11y features enabled/disabled
262 // by the user on the login screen are enabled/disabled in the user's profile.
263 // This function copies settings from the signin prefs into the user's prefs
264 // when it detects a login with a newly created profile.
CopySigninPrefsIfNeeded(PrefService * previous_pref_service,PrefService * current_pref_service)265 void CopySigninPrefsIfNeeded(PrefService* previous_pref_service,
266                              PrefService* current_pref_service) {
267   DCHECK(current_pref_service);
268   if (!ShouldCopySigninPrefs(previous_pref_service, current_pref_service))
269     return;
270 
271   PrefService* signin_prefs =
272       Shell::Get()->session_controller()->GetSigninScreenPrefService();
273   DCHECK(signin_prefs);
274   for (const auto* pref_path : kCopiedOnSigninAccessibilityPrefs) {
275     const PrefService::Preference* pref =
276         signin_prefs->FindPreference(pref_path);
277 
278     // Ignore if the pref has not been set by the user.
279     if (!pref || !pref->IsUserControlled())
280       continue;
281 
282     // Copy the pref value from the signin profile.
283     const base::Value* value_on_login = pref->GetValue();
284     current_pref_service->Set(pref_path, *value_on_login);
285   }
286 }
287 
288 // Used to indicate which accessibility notification should be shown.
289 enum class A11yNotificationType {
290   // No accessibility notification.
291   kNone,
292   // Shown when spoken feedback is set enabled with A11Y_NOTIFICATION_SHOW.
293   kSpokenFeedbackEnabled,
294   // Shown when braille display is connected while spoken feedback is enabled.
295   kBrailleDisplayConnected,
296   // Shown when braille display is connected while spoken feedback is not
297   // enabled yet. Note: in this case braille display connected would enable
298   // spoken feeback.
299   kSpokenFeedbackBrailleEnabled,
300   // Shown when Switch Access is enabled.
301   kSwitchAccessEnabled,
302 };
303 
304 // Returns notification icon based on the A11yNotificationType.
GetNotificationIcon(A11yNotificationType type)305 const gfx::VectorIcon& GetNotificationIcon(A11yNotificationType type) {
306   switch (type) {
307     case A11yNotificationType::kSpokenFeedbackBrailleEnabled:
308       return kNotificationAccessibilityIcon;
309     case A11yNotificationType::kBrailleDisplayConnected:
310       return kNotificationAccessibilityBrailleIcon;
311     case A11yNotificationType::kSwitchAccessEnabled:
312       return kSwitchAccessIcon;
313     default:
314       return kNotificationChromevoxIcon;
315   }
316 }
317 
ShowAccessibilityNotification(A11yNotificationType type)318 void ShowAccessibilityNotification(A11yNotificationType type) {
319   message_center::MessageCenter* message_center =
320       message_center::MessageCenter::Get();
321   message_center->RemoveNotification(kNotificationId, false /* by_user */);
322 
323   if (type == A11yNotificationType::kNone)
324     return;
325 
326   base::string16 text;
327   base::string16 title;
328   if (type == A11yNotificationType::kBrailleDisplayConnected) {
329     text = l10n_util::GetStringUTF16(
330         IDS_ASH_STATUS_TRAY_BRAILLE_DISPLAY_CONNECTED);
331   } else if (type == A11yNotificationType::kSwitchAccessEnabled) {
332     title = l10n_util::GetStringUTF16(
333         IDS_ASH_STATUS_TRAY_SWITCH_ACCESS_ENABLED_TITLE);
334     text = l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SWITCH_ACCESS_ENABLED);
335   } else {
336     bool is_tablet = Shell::Get()->tablet_mode_controller()->InTabletMode();
337 
338     title = l10n_util::GetStringUTF16(
339         type == A11yNotificationType::kSpokenFeedbackBrailleEnabled
340             ? IDS_ASH_STATUS_TRAY_SPOKEN_FEEDBACK_BRAILLE_ENABLED_TITLE
341             : IDS_ASH_STATUS_TRAY_SPOKEN_FEEDBACK_ENABLED_TITLE);
342     text = l10n_util::GetStringUTF16(
343         is_tablet ? IDS_ASH_STATUS_TRAY_SPOKEN_FEEDBACK_ENABLED_TABLET
344                   : IDS_ASH_STATUS_TRAY_SPOKEN_FEEDBACK_ENABLED);
345   }
346   message_center::RichNotificationData options;
347   options.should_make_spoken_feedback_for_popup_updates = false;
348   std::unique_ptr<message_center::Notification> notification =
349       ash::CreateSystemNotification(
350           message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId, title,
351           text, base::string16(), GURL(),
352           message_center::NotifierId(
353               message_center::NotifierType::SYSTEM_COMPONENT,
354               kNotifierAccessibility),
355           options, nullptr, GetNotificationIcon(type),
356           message_center::SystemNotificationWarningLevel::NORMAL);
357   notification->set_pinned(true);
358   message_center->AddNotification(std::move(notification));
359 }
360 
GetLayoutManager()361 AccessibilityPanelLayoutManager* GetLayoutManager() {
362   // The accessibility panel is only shown on the primary display.
363   aura::Window* root = Shell::GetPrimaryRootWindow();
364   aura::Window* container =
365       Shell::GetContainer(root, kShellWindowId_AccessibilityPanelContainer);
366   // TODO(jamescook): Avoid this cast by moving ash::AccessibilityObserver
367   // ownership to this class and notifying it on accessibility panel fullscreen
368   // updates.
369   return static_cast<AccessibilityPanelLayoutManager*>(
370       container->layout_manager());
371 }
372 
PrefKeyForSwitchAccessCommand(SwitchAccessCommand command)373 std::string PrefKeyForSwitchAccessCommand(SwitchAccessCommand command) {
374   switch (command) {
375     case SwitchAccessCommand::kSelect:
376       return prefs::kAccessibilitySwitchAccessSelectKeyCodes;
377     case SwitchAccessCommand::kNext:
378       return prefs::kAccessibilitySwitchAccessNextKeyCodes;
379     case SwitchAccessCommand::kPrevious:
380       return prefs::kAccessibilitySwitchAccessPreviousKeyCodes;
381     case SwitchAccessCommand::kNone:
382       NOTREACHED();
383       return "";
384   }
385 }
386 
UmaNameForSwitchAccessCommand(SwitchAccessCommand command)387 std::string UmaNameForSwitchAccessCommand(SwitchAccessCommand command) {
388   switch (command) {
389     case SwitchAccessCommand::kSelect:
390       return "Accessibility.CrosSwitchAccess.SelectKeyCode";
391     case SwitchAccessCommand::kNext:
392       return "Accessibility.CrosSwitchAccess.NextKeyCode";
393     case SwitchAccessCommand::kPrevious:
394       return "Accessibility.CrosSwitchAccess.PreviousKeyCode";
395     case SwitchAccessCommand::kNone:
396       NOTREACHED();
397       return "";
398   }
399 }
400 
401 // These values are persisted to logs. Entries should not be renumbered and
402 // numeric values should never be reused.
403 enum class SwitchAccessCommandKeyCode {
404   kUnknown = 0,
405   kNone = 1,
406   kSpace = 2,
407   kEnter = 3,
408   kMaxValue = kEnter,
409 };
410 
UmaValueForKeyCode(int key_code)411 SwitchAccessCommandKeyCode UmaValueForKeyCode(int key_code) {
412   switch (key_code) {
413     case 0:
414       return SwitchAccessCommandKeyCode::kNone;
415     case 13:
416       return SwitchAccessCommandKeyCode::kEnter;
417     case 32:
418       return SwitchAccessCommandKeyCode::kSpace;
419     default:
420       return SwitchAccessCommandKeyCode::kUnknown;
421   }
422 }
423 
424 }  // namespace
425 
Feature(FeatureType type,const std::string & pref_name,const gfx::VectorIcon * icon,AccessibilityControllerImpl * controller)426 AccessibilityControllerImpl::Feature::Feature(
427     FeatureType type,
428     const std::string& pref_name,
429     const gfx::VectorIcon* icon,
430     AccessibilityControllerImpl* controller)
431     : type_(type), pref_name_(pref_name), icon_(icon), owner_(controller) {}
432 
433 AccessibilityControllerImpl::Feature::~Feature() = default;
434 
SetEnabled(bool enabled)435 void AccessibilityControllerImpl::Feature::SetEnabled(bool enabled) {
436   PrefService* prefs = owner_->active_user_prefs_;
437   if (!prefs)
438     return;
439   prefs->SetBoolean(pref_name_, enabled);
440   prefs->CommitPendingWrite();
441 }
442 
IsVisibleInTray() const443 bool AccessibilityControllerImpl::Feature::IsVisibleInTray() const {
444   return (conflicting_feature_ == kNoConflictingFeature ||
445           !owner_->GetFeature(conflicting_feature_).enabled()) &&
446          owner_->IsAccessibilityFeatureVisibleInTrayMenu(pref_name_);
447 }
448 
IsEnterpriseIconVisible() const449 bool AccessibilityControllerImpl::Feature::IsEnterpriseIconVisible() const {
450   return owner_->IsEnterpriseIconVisibleInTrayMenu(pref_name_);
451 }
452 
icon() const453 const gfx::VectorIcon& AccessibilityControllerImpl::Feature::icon() const {
454   DCHECK(icon_);
455   if (icon_)
456     return *icon_;
457   return kPaletteTrayIconDefaultIcon;
458 }
459 
UpdateFromPref()460 void AccessibilityControllerImpl::Feature::UpdateFromPref() {
461   PrefService* prefs = owner_->active_user_prefs_;
462   DCHECK(prefs);
463 
464   bool enabled = prefs->GetBoolean(pref_name_);
465 
466   if (conflicting_feature_ != FeatureType::kNoConflictingFeature &&
467       owner_->GetFeature(conflicting_feature_).enabled()) {
468     enabled = false;
469   }
470 
471   if (enabled == enabled_)
472     return;
473 
474   enabled_ = enabled;
475   owner_->UpdateFeatureFromPref(type_);
476 }
477 
SetConflictingFeature(AccessibilityControllerImpl::FeatureType feature)478 void AccessibilityControllerImpl::Feature::SetConflictingFeature(
479     AccessibilityControllerImpl::FeatureType feature) {
480   DCHECK_EQ(conflicting_feature_, FeatureType::kNoConflictingFeature);
481   conflicting_feature_ = feature;
482 }
483 
FeatureWithDialog(FeatureType type,const std::string & pref_name,const gfx::VectorIcon * icon,const Dialog & dialog,AccessibilityControllerImpl * controller)484 AccessibilityControllerImpl::FeatureWithDialog::FeatureWithDialog(
485     FeatureType type,
486     const std::string& pref_name,
487     const gfx::VectorIcon* icon,
488     const Dialog& dialog,
489     AccessibilityControllerImpl* controller)
490     : AccessibilityControllerImpl::Feature(type, pref_name, icon, controller),
491       dialog_(dialog) {}
492 AccessibilityControllerImpl::FeatureWithDialog::~FeatureWithDialog() = default;
493 
SetDialogAccepted()494 void AccessibilityControllerImpl::FeatureWithDialog::SetDialogAccepted() {
495   PrefService* prefs = owner_->active_user_prefs_;
496   if (!prefs)
497     return;
498   prefs->SetBoolean(dialog_.pref_name, true);
499   prefs->CommitPendingWrite();
500 }
501 
WasDialogAccepted() const502 bool AccessibilityControllerImpl::FeatureWithDialog::WasDialogAccepted() const {
503   PrefService* prefs = owner_->active_user_prefs_;
504   DCHECK(prefs);
505   return prefs->GetBoolean(dialog_.pref_name);
506 }
507 
SetEnabledWithDialog(bool enabled,base::OnceClosure completion_callback)508 void AccessibilityControllerImpl::FeatureWithDialog::SetEnabledWithDialog(
509     bool enabled,
510     base::OnceClosure completion_callback) {
511   PrefService* prefs = owner_->active_user_prefs_;
512   if (!prefs)
513     return;
514   // We should not show the dialog when the feature is already enabled.
515   if (enabled && !this->enabled() && !WasDialogAccepted()) {
516     Shell::Get()->accelerator_controller()->MaybeShowConfirmationDialog(
517         dialog_.title_resource_id, dialog_.body_resource_id,
518         // Callback for if the user accepts the dialog
519         base::BindOnce(
520             [](base::WeakPtr<AccessibilityControllerImpl> owner,
521                FeatureType type, base::OnceClosure completion_callback) {
522               if (!owner)
523                 return;
524 
525               static_cast<FeatureWithDialog&>(owner->GetFeature(type))
526                   .SetDialogAccepted();
527               // If they accept, try again to set value to true
528               owner->GetFeature(type).SetEnabled(true);
529               std::move(completion_callback).Run();
530             },
531             owner_->weak_ptr_factory_.GetWeakPtr(), type_,
532             std::move(completion_callback)),
533         base::DoNothing());
534 
535     return;
536   }
537   Feature::SetEnabled(enabled);
538   std::move(completion_callback).Run();
539 }
540 
SetEnabled(bool enabled)541 void AccessibilityControllerImpl::FeatureWithDialog::SetEnabled(bool enabled) {
542   if (dialog_.mandatory)
543     SetEnabledWithDialog(enabled, base::DoNothing());
544   else
545     Feature::SetEnabled(enabled);
546 }
547 
AccessibilityControllerImpl()548 AccessibilityControllerImpl::AccessibilityControllerImpl()
549     : autoclick_delay_(AutoclickController::GetDefaultAutoclickDelay()) {
550   Shell::Get()->session_controller()->AddObserver(this);
551   Shell::Get()->tablet_mode_controller()->AddObserver(this);
552   CreateAccessibilityFeatures();
553 }
554 
~AccessibilityControllerImpl()555 AccessibilityControllerImpl::~AccessibilityControllerImpl() {
556   floating_menu_controller_.reset();
557 }
558 
CreateAccessibilityFeatures()559 void AccessibilityControllerImpl::CreateAccessibilityFeatures() {
560   DCHECK(VerifyFeaturesData());
561   // First, build all features with dialog.
562   std::map<FeatureType, Dialog> dialogs;
563   for (auto dialog_data : kFeatureDialogs) {
564     dialogs[dialog_data.type] = {dialog_data.pref, dialog_data.title,
565                                  dialog_data.body, dialog_data.mandatory};
566   }
567   for (auto feature_data : kFeatures) {
568     DCHECK(!features_[feature_data.type]);
569     auto it = dialogs.find(feature_data.type);
570     if (it == dialogs.end()) {
571       features_[feature_data.type] = std::make_unique<Feature>(
572           feature_data.type, feature_data.pref, feature_data.icon, this);
573     } else {
574       features_[feature_data.type] = std::make_unique<FeatureWithDialog>(
575           feature_data.type, feature_data.pref, feature_data.icon, it->second,
576           this);
577     }
578   }
579 }
580 
581 // static
RegisterProfilePrefs(PrefRegistrySimple * registry)582 void AccessibilityControllerImpl::RegisterProfilePrefs(
583     PrefRegistrySimple* registry) {
584   registry->RegisterBooleanPref(
585       prefs::kAccessibilityAutoclickEnabled, false,
586       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
587   registry->RegisterIntegerPref(
588       prefs::kAccessibilityAutoclickDelayMs, kDefaultAutoclickDelayMs,
589       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
590   registry->RegisterIntegerPref(
591       prefs::kAccessibilityAutoclickEventType,
592       static_cast<int>(kDefaultAutoclickEventType),
593       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
594   registry->RegisterBooleanPref(
595       prefs::kAccessibilityAutoclickRevertToLeftClick, true,
596       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
597   registry->RegisterBooleanPref(
598       prefs::kAccessibilityAutoclickStabilizePosition, false,
599       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
600   registry->RegisterIntegerPref(
601       prefs::kAccessibilityAutoclickMovementThreshold,
602       kDefaultAutoclickMovementThreshold,
603       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
604   registry->RegisterIntegerPref(
605       prefs::kAccessibilityAutoclickMenuPosition,
606       static_cast<int>(kDefaultAutoclickMenuPosition),
607       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
608   registry->RegisterBooleanPref(
609       prefs::kAccessibilityCaretHighlightEnabled, false,
610       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
611   registry->RegisterBooleanPref(
612       prefs::kAccessibilityCursorHighlightEnabled, false,
613       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
614   registry->RegisterBooleanPref(
615       prefs::kAccessibilityCursorColorEnabled, false,
616       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
617   registry->RegisterIntegerPref(
618       prefs::kAccessibilityCursorColor, 0,
619       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
620   registry->RegisterBooleanPref(
621       prefs::kAccessibilityDictationEnabled, false,
622       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
623   registry->RegisterBooleanPref(
624       prefs::kAccessibilityFloatingMenuEnabled, false,
625       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
626   registry->RegisterIntegerPref(
627       prefs::kAccessibilityFloatingMenuPosition,
628       static_cast<int>(kDefaultFloatingMenuPosition),
629       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
630   registry->RegisterBooleanPref(
631       prefs::kAccessibilityFocusHighlightEnabled, false,
632       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
633   registry->RegisterBooleanPref(
634       prefs::kAccessibilityHighContrastEnabled, false,
635       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
636   registry->RegisterBooleanPref(
637       prefs::kAccessibilityLargeCursorEnabled, false,
638       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
639   registry->RegisterIntegerPref(prefs::kAccessibilityLargeCursorDipSize,
640                                 kDefaultLargeCursorSize);
641   registry->RegisterBooleanPref(
642       prefs::kAccessibilityMonoAudioEnabled, false,
643       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
644   registry->RegisterBooleanPref(
645       prefs::kAccessibilityScreenMagnifierCenterFocus, true,
646       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
647   registry->RegisterBooleanPref(
648       prefs::kAccessibilityScreenMagnifierEnabled, false,
649       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
650   registry->RegisterBooleanPref(
651       prefs::kAccessibilityScreenMagnifierFocusFollowingEnabled, true,
652       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
653   registry->RegisterDoublePref(prefs::kAccessibilityScreenMagnifierScale,
654                                std::numeric_limits<double>::min());
655   registry->RegisterBooleanPref(prefs::kAccessibilitySpokenFeedbackEnabled,
656                                 false);
657   registry->RegisterBooleanPref(
658       prefs::kAccessibilitySelectToSpeakEnabled, false,
659       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
660   registry->RegisterBooleanPref(
661       prefs::kAccessibilityStickyKeysEnabled, false,
662       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
663   registry->RegisterBooleanPref(
664       prefs::kAccessibilityShortcutsEnabled, true,
665       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
666   registry->RegisterBooleanPref(
667       prefs::kAccessibilitySwitchAccessEnabled, false,
668       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
669   registry->RegisterListPref(
670       prefs::kAccessibilitySwitchAccessSelectKeyCodes,
671       base::Value(std::vector<base::Value>()),
672       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
673   registry->RegisterIntegerPref(
674       prefs::kAccessibilitySwitchAccessSelectSetting,
675       kSwitchAccessAssignmentNone,
676       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
677   registry->RegisterListPref(
678       prefs::kAccessibilitySwitchAccessNextKeyCodes,
679       base::Value(std::vector<base::Value>()),
680       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
681   registry->RegisterIntegerPref(
682       prefs::kAccessibilitySwitchAccessNextSetting, kSwitchAccessAssignmentNone,
683       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
684   registry->RegisterListPref(
685       prefs::kAccessibilitySwitchAccessPreviousKeyCodes,
686       base::Value(std::vector<base::Value>()),
687       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
688   registry->RegisterIntegerPref(
689       prefs::kAccessibilitySwitchAccessPreviousSetting,
690       kSwitchAccessAssignmentNone,
691       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
692   registry->RegisterBooleanPref(
693       prefs::kAccessibilitySwitchAccessAutoScanEnabled, false,
694       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
695   registry->RegisterIntegerPref(
696       prefs::kAccessibilitySwitchAccessAutoScanSpeedMs,
697       kDefaultSwitchAccessAutoScanSpeed.InMilliseconds(),
698       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
699   registry->RegisterIntegerPref(
700       prefs::kAccessibilitySwitchAccessAutoScanKeyboardSpeedMs,
701       kDefaultSwitchAccessAutoScanSpeed.InMilliseconds(),
702       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
703   registry->RegisterBooleanPref(
704       prefs::kAccessibilityVirtualKeyboardEnabled, false,
705       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
706   registry->RegisterBooleanPref(
707       prefs::kAccessibilityTabletModeShelfNavigationButtonsEnabled, false,
708       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
709   registry->RegisterBooleanPref(
710       prefs::kHighContrastAcceleratorDialogHasBeenAccepted, false);
711   registry->RegisterBooleanPref(
712       prefs::kScreenMagnifierAcceleratorDialogHasBeenAccepted, false);
713   registry->RegisterBooleanPref(
714       prefs::kDockedMagnifierAcceleratorDialogHasBeenAccepted, false);
715   registry->RegisterBooleanPref(
716       prefs::kDictationAcceleratorDialogHasBeenAccepted, false);
717   registry->RegisterBooleanPref(
718       prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted2, false);
719 
720   registry->RegisterBooleanPref(
721       prefs::kShouldAlwaysShowAccessibilityMenu, false,
722       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
723 }
724 
Shutdown()725 void AccessibilityControllerImpl::Shutdown() {
726   Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
727   Shell::Get()->session_controller()->RemoveObserver(this);
728 
729   for (auto& observer : observers_)
730     observer.OnAccessibilityControllerShutdown();
731 }
732 
733 bool AccessibilityControllerImpl::
HasDisplayRotationAcceleratorDialogBeenAccepted() const734     HasDisplayRotationAcceleratorDialogBeenAccepted() const {
735   return active_user_prefs_ &&
736          active_user_prefs_->GetBoolean(
737              prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted2);
738 }
739 
740 void AccessibilityControllerImpl::
SetDisplayRotationAcceleratorDialogBeenAccepted()741     SetDisplayRotationAcceleratorDialogBeenAccepted() {
742   if (!active_user_prefs_)
743     return;
744   active_user_prefs_->SetBoolean(
745       prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted2, true);
746   active_user_prefs_->CommitPendingWrite();
747 }
748 
AddObserver(AccessibilityObserver * observer)749 void AccessibilityControllerImpl::AddObserver(AccessibilityObserver* observer) {
750   observers_.AddObserver(observer);
751 }
752 
RemoveObserver(AccessibilityObserver * observer)753 void AccessibilityControllerImpl::RemoveObserver(
754     AccessibilityObserver* observer) {
755   observers_.RemoveObserver(observer);
756 }
757 
GetFeature(FeatureType type) const758 AccessibilityControllerImpl::Feature& AccessibilityControllerImpl::GetFeature(
759     FeatureType type) const {
760   DCHECK(features_[type].get());
761   return *features_[type].get();
762 }
763 
autoclick() const764 AccessibilityControllerImpl::Feature& AccessibilityControllerImpl::autoclick()
765     const {
766   return GetFeature(FeatureType::kAutoclick);
767 }
768 
769 AccessibilityControllerImpl::Feature&
caret_highlight() const770 AccessibilityControllerImpl::caret_highlight() const {
771   return GetFeature(FeatureType::kCaretHighlight);
772 }
773 
774 AccessibilityControllerImpl::Feature&
cursor_highlight() const775 AccessibilityControllerImpl::cursor_highlight() const {
776   return GetFeature(FeatureType::KCursorHighlight);
777 }
778 
779 AccessibilityControllerImpl::Feature&
cursor_color() const780 AccessibilityControllerImpl::cursor_color() const {
781   return GetFeature(FeatureType::kCursorColor);
782 }
783 
784 AccessibilityControllerImpl::FeatureWithDialog&
dictation() const785 AccessibilityControllerImpl::dictation() const {
786   return static_cast<FeatureWithDialog&>(GetFeature(FeatureType::kDictation));
787 }
788 
789 AccessibilityControllerImpl::Feature&
focus_highlight() const790 AccessibilityControllerImpl::focus_highlight() const {
791   return GetFeature(FeatureType::kFocusHighlight);
792 }
793 
794 AccessibilityControllerImpl::Feature&
floating_menu() const795 AccessibilityControllerImpl::floating_menu() const {
796   return GetFeature(FeatureType::kFloatingMenu);
797 }
798 
799 AccessibilityControllerImpl::FeatureWithDialog&
fullscreen_magnifier() const800 AccessibilityControllerImpl::fullscreen_magnifier() const {
801   return static_cast<FeatureWithDialog&>(
802       GetFeature(FeatureType::kFullscreenMagnifier));
803 }
804 
805 AccessibilityControllerImpl::FeatureWithDialog&
docked_magnifier() const806 AccessibilityControllerImpl::docked_magnifier() const {
807   return static_cast<FeatureWithDialog&>(
808       GetFeature(FeatureType::kDockedMagnifier));
809 }
810 
811 AccessibilityControllerImpl::FeatureWithDialog&
high_contrast() const812 AccessibilityControllerImpl::high_contrast() const {
813   return static_cast<FeatureWithDialog&>(
814       GetFeature(FeatureType::kHighContrast));
815 }
816 
817 AccessibilityControllerImpl::Feature&
large_cursor() const818 AccessibilityControllerImpl::large_cursor() const {
819   return GetFeature(FeatureType::kLargeCursor);
820 }
821 
mono_audio() const822 AccessibilityControllerImpl::Feature& AccessibilityControllerImpl::mono_audio()
823     const {
824   return GetFeature(FeatureType::kMonoAudio);
825 }
826 
827 AccessibilityControllerImpl::Feature&
spoken_feedback() const828 AccessibilityControllerImpl::spoken_feedback() const {
829   return GetFeature(FeatureType::kSpokenFeedback);
830 }
831 
832 AccessibilityControllerImpl::Feature&
select_to_speak() const833 AccessibilityControllerImpl::select_to_speak() const {
834   return GetFeature(FeatureType::kSelectToSpeak);
835 }
836 
sticky_keys() const837 AccessibilityControllerImpl::Feature& AccessibilityControllerImpl::sticky_keys()
838     const {
839   return GetFeature(FeatureType::kStickyKeys);
840 }
841 
842 AccessibilityControllerImpl::Feature&
switch_access() const843 AccessibilityControllerImpl::switch_access() const {
844   return GetFeature(FeatureType::kSwitchAccess);
845 }
846 
847 AccessibilityControllerImpl::Feature&
virtual_keyboard() const848 AccessibilityControllerImpl::virtual_keyboard() const {
849   return GetFeature(FeatureType::kVirtualKeyboard);
850 }
851 
IsAutoclickSettingVisibleInTray()852 bool AccessibilityControllerImpl::IsAutoclickSettingVisibleInTray() {
853   return autoclick().IsVisibleInTray();
854 }
855 
IsEnterpriseIconVisibleForAutoclick()856 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleForAutoclick() {
857   return autoclick().IsEnterpriseIconVisible();
858 }
859 
IsPrimarySettingsViewVisibleInTray()860 bool AccessibilityControllerImpl::IsPrimarySettingsViewVisibleInTray() {
861   return (IsSpokenFeedbackSettingVisibleInTray() ||
862           IsSelectToSpeakSettingVisibleInTray() ||
863           IsDictationSettingVisibleInTray() ||
864           IsHighContrastSettingVisibleInTray() ||
865           IsFullScreenMagnifierSettingVisibleInTray() ||
866           IsDockedMagnifierSettingVisibleInTray() ||
867           IsAutoclickSettingVisibleInTray() ||
868           IsVirtualKeyboardSettingVisibleInTray() ||
869           IsSwitchAccessSettingVisibleInTray());
870 }
871 
IsAdditionalSettingsViewVisibleInTray()872 bool AccessibilityControllerImpl::IsAdditionalSettingsViewVisibleInTray() {
873   return (IsLargeCursorSettingVisibleInTray() ||
874           IsMonoAudioSettingVisibleInTray() ||
875           IsCaretHighlightSettingVisibleInTray() ||
876           IsCursorHighlightSettingVisibleInTray() ||
877           IsFocusHighlightSettingVisibleInTray() ||
878           IsStickyKeysSettingVisibleInTray());
879 }
880 
IsAdditionalSettingsSeparatorVisibleInTray()881 bool AccessibilityControllerImpl::IsAdditionalSettingsSeparatorVisibleInTray() {
882   return IsPrimarySettingsViewVisibleInTray() &&
883          IsAdditionalSettingsViewVisibleInTray();
884 }
885 
IsCaretHighlightSettingVisibleInTray()886 bool AccessibilityControllerImpl::IsCaretHighlightSettingVisibleInTray() {
887   return caret_highlight().IsVisibleInTray();
888 }
889 
IsEnterpriseIconVisibleForCaretHighlight()890 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleForCaretHighlight() {
891   return caret_highlight().IsEnterpriseIconVisible();
892 }
893 
IsCursorHighlightSettingVisibleInTray()894 bool AccessibilityControllerImpl::IsCursorHighlightSettingVisibleInTray() {
895   return cursor_highlight().IsVisibleInTray();
896 }
897 
IsEnterpriseIconVisibleForCursorHighlight()898 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleForCursorHighlight() {
899   return cursor_highlight().IsEnterpriseIconVisible();
900 }
901 
IsDictationSettingVisibleInTray()902 bool AccessibilityControllerImpl::IsDictationSettingVisibleInTray() {
903   return dictation().IsVisibleInTray();
904 }
905 
IsEnterpriseIconVisibleForDictation()906 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleForDictation() {
907   return dictation().IsEnterpriseIconVisible();
908 }
909 
IsFocusHighlightSettingVisibleInTray()910 bool AccessibilityControllerImpl::IsFocusHighlightSettingVisibleInTray() {
911   return focus_highlight().IsVisibleInTray();
912 }
913 
IsEnterpriseIconVisibleForFocusHighlight()914 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleForFocusHighlight() {
915   return focus_highlight().IsEnterpriseIconVisible();
916 }
917 
IsFullScreenMagnifierSettingVisibleInTray()918 bool AccessibilityControllerImpl::IsFullScreenMagnifierSettingVisibleInTray() {
919   return fullscreen_magnifier().IsVisibleInTray();
920 }
921 
922 bool AccessibilityControllerImpl::
IsEnterpriseIconVisibleForFullScreenMagnifier()923     IsEnterpriseIconVisibleForFullScreenMagnifier() {
924   return fullscreen_magnifier().IsEnterpriseIconVisible();
925 }
926 
IsDockedMagnifierSettingVisibleInTray()927 bool AccessibilityControllerImpl::IsDockedMagnifierSettingVisibleInTray() {
928   return docked_magnifier().IsVisibleInTray();
929 }
930 
IsEnterpriseIconVisibleForDockedMagnifier()931 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleForDockedMagnifier() {
932   return docked_magnifier().IsEnterpriseIconVisible();
933 }
934 
IsHighContrastSettingVisibleInTray()935 bool AccessibilityControllerImpl::IsHighContrastSettingVisibleInTray() {
936   return high_contrast().IsVisibleInTray();
937 }
938 
IsEnterpriseIconVisibleForHighContrast()939 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleForHighContrast() {
940   return high_contrast().IsEnterpriseIconVisible();
941 }
942 
IsLargeCursorSettingVisibleInTray()943 bool AccessibilityControllerImpl::IsLargeCursorSettingVisibleInTray() {
944   return large_cursor().IsVisibleInTray();
945 }
946 
IsEnterpriseIconVisibleForLargeCursor()947 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleForLargeCursor() {
948   return large_cursor().IsEnterpriseIconVisible();
949 }
950 
IsMonoAudioSettingVisibleInTray()951 bool AccessibilityControllerImpl::IsMonoAudioSettingVisibleInTray() {
952   return mono_audio().IsVisibleInTray();
953 }
954 
IsEnterpriseIconVisibleForMonoAudio()955 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleForMonoAudio() {
956   return mono_audio().IsEnterpriseIconVisible();
957 }
958 
SetSpokenFeedbackEnabled(bool enabled,AccessibilityNotificationVisibility notify)959 void AccessibilityControllerImpl::SetSpokenFeedbackEnabled(
960     bool enabled,
961     AccessibilityNotificationVisibility notify) {
962   spoken_feedback().SetEnabled(enabled);
963 
964   // Value could be left unchanged because of higher-priority pref source, eg.
965   // policy. See crbug.com/953245.
966   const bool actual_enabled = active_user_prefs_->GetBoolean(
967       prefs::kAccessibilitySpokenFeedbackEnabled);
968 
969   A11yNotificationType type = A11yNotificationType::kNone;
970   if (enabled && actual_enabled && notify == A11Y_NOTIFICATION_SHOW)
971     type = A11yNotificationType::kSpokenFeedbackEnabled;
972   ShowAccessibilityNotification(type);
973 }
974 
IsSpokenFeedbackSettingVisibleInTray()975 bool AccessibilityControllerImpl::IsSpokenFeedbackSettingVisibleInTray() {
976   return spoken_feedback().IsVisibleInTray();
977 }
978 
IsEnterpriseIconVisibleForSpokenFeedback()979 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleForSpokenFeedback() {
980   return spoken_feedback().IsEnterpriseIconVisible();
981 }
982 
IsSelectToSpeakSettingVisibleInTray()983 bool AccessibilityControllerImpl::IsSelectToSpeakSettingVisibleInTray() {
984   return select_to_speak().IsVisibleInTray();
985 }
986 
IsEnterpriseIconVisibleForSelectToSpeak()987 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleForSelectToSpeak() {
988   return select_to_speak().IsEnterpriseIconVisible();
989 }
990 
RequestSelectToSpeakStateChange()991 void AccessibilityControllerImpl::RequestSelectToSpeakStateChange() {
992   client_->RequestSelectToSpeakStateChange();
993 }
994 
SetSelectToSpeakState(SelectToSpeakState state)995 void AccessibilityControllerImpl::SetSelectToSpeakState(
996     SelectToSpeakState state) {
997   select_to_speak_state_ = state;
998 
999   // Forward the state change event to select_to_speak_event_handler_.
1000   // The extension may have requested that the handler enter SELECTING state.
1001   // Prepare to start capturing events from stylus, mouse or touch.
1002   if (select_to_speak_event_handler_) {
1003     select_to_speak_event_handler_->SetSelectToSpeakStateSelecting(
1004         state == SelectToSpeakState::kSelectToSpeakStateSelecting);
1005   }
1006   NotifyAccessibilityStatusChanged();
1007 }
1008 
SetSelectToSpeakEventHandlerDelegate(SelectToSpeakEventHandlerDelegate * delegate)1009 void AccessibilityControllerImpl::SetSelectToSpeakEventHandlerDelegate(
1010     SelectToSpeakEventHandlerDelegate* delegate) {
1011   select_to_speak_event_handler_delegate_ = delegate;
1012   MaybeCreateSelectToSpeakEventHandler();
1013 }
1014 
GetSelectToSpeakState() const1015 SelectToSpeakState AccessibilityControllerImpl::GetSelectToSpeakState() const {
1016   return select_to_speak_state_;
1017 }
1018 
ShowSelectToSpeakPanel(const gfx::Rect & anchor,bool is_paused)1019 void AccessibilityControllerImpl::ShowSelectToSpeakPanel(
1020     const gfx::Rect& anchor,
1021     bool is_paused) {
1022   if (!features::IsSelectToSpeakNavigationControlEnabled()) {
1023     return;
1024   }
1025   if (!select_to_speak_bubble_controller_) {
1026     select_to_speak_bubble_controller_ =
1027         std::make_unique<SelectToSpeakMenuBubbleController>();
1028   }
1029   select_to_speak_bubble_controller_->Show(anchor, is_paused);
1030 }
1031 
HideSelectToSpeakPanel()1032 void AccessibilityControllerImpl::HideSelectToSpeakPanel() {
1033   if (!features::IsSelectToSpeakNavigationControlEnabled() ||
1034       !select_to_speak_bubble_controller_) {
1035     return;
1036   }
1037   select_to_speak_bubble_controller_->Hide();
1038 }
1039 
IsSwitchAccessRunning() const1040 bool AccessibilityControllerImpl::IsSwitchAccessRunning() const {
1041   return switch_access().enabled() || switch_access_disable_dialog_showing_;
1042 }
1043 
IsSwitchAccessSettingVisibleInTray()1044 bool AccessibilityControllerImpl::IsSwitchAccessSettingVisibleInTray() {
1045   // Switch Access cannot be enabled on the sign-in page because there is no way
1046   // to configure switches while the device is locked.
1047   if (!switch_access().enabled() &&
1048       Shell::Get()->session_controller()->login_status() ==
1049           ash::LoginStatus::NOT_LOGGED_IN) {
1050     return false;
1051   }
1052   return switch_access().IsVisibleInTray();
1053   return IsEnterpriseIconVisibleInTrayMenu(
1054       prefs::kAccessibilitySwitchAccessEnabled);
1055 }
1056 
IsEnterpriseIconVisibleForSwitchAccess()1057 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleForSwitchAccess() {
1058   return switch_access().IsEnterpriseIconVisible();
1059 }
1060 
SetAccessibilityEventRewriter(AccessibilityEventRewriter * accessibility_event_rewriter)1061 void AccessibilityControllerImpl::SetAccessibilityEventRewriter(
1062     AccessibilityEventRewriter* accessibility_event_rewriter) {
1063   accessibility_event_rewriter_ = accessibility_event_rewriter;
1064   if (accessibility_event_rewriter_)
1065     UpdateKeyCodesAfterSwitchAccessEnabled();
1066 }
1067 
HideSwitchAccessBackButton()1068 void AccessibilityControllerImpl::HideSwitchAccessBackButton() {
1069   if (IsSwitchAccessRunning())
1070     switch_access_bubble_controller_->HideBackButton();
1071 }
1072 
HideSwitchAccessMenu()1073 void AccessibilityControllerImpl::HideSwitchAccessMenu() {
1074   if (IsSwitchAccessRunning())
1075     switch_access_bubble_controller_->HideMenuBubble();
1076 }
1077 
ShowSwitchAccessBackButton(const gfx::Rect & anchor)1078 void AccessibilityControllerImpl::ShowSwitchAccessBackButton(
1079     const gfx::Rect& anchor) {
1080   switch_access_bubble_controller_->ShowBackButton(anchor);
1081 }
1082 
ShowSwitchAccessMenu(const gfx::Rect & anchor,std::vector<std::string> actions_to_show)1083 void AccessibilityControllerImpl::ShowSwitchAccessMenu(
1084     const gfx::Rect& anchor,
1085     std::vector<std::string> actions_to_show) {
1086   switch_access_bubble_controller_->ShowMenu(anchor, actions_to_show);
1087 }
1088 
IsPointScanEnabled()1089 bool AccessibilityControllerImpl::IsPointScanEnabled() {
1090   return point_scan_controller_.get() &&
1091          point_scan_controller_->IsPointScanEnabled();
1092 }
1093 
ActivatePointScan()1094 void AccessibilityControllerImpl::ActivatePointScan() {
1095   if (::switches::IsSwitchAccessPointScanningEnabled()) {
1096     point_scan_controller_ = std::make_unique<PointScanController>();
1097     point_scan_controller_->Start();
1098   }
1099 }
1100 
1101 void AccessibilityControllerImpl::
DisablePolicyRecommendationRestorerForTesting()1102     DisablePolicyRecommendationRestorerForTesting() {
1103   Shell::Get()->policy_recommendation_restorer()->DisableForTesting();
1104 }
1105 
IsStickyKeysSettingVisibleInTray()1106 bool AccessibilityControllerImpl::IsStickyKeysSettingVisibleInTray() {
1107   return sticky_keys().IsVisibleInTray();
1108 }
1109 
IsEnterpriseIconVisibleForStickyKeys()1110 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleForStickyKeys() {
1111   return sticky_keys().IsEnterpriseIconVisible();
1112 }
1113 
IsVirtualKeyboardSettingVisibleInTray()1114 bool AccessibilityControllerImpl::IsVirtualKeyboardSettingVisibleInTray() {
1115   return virtual_keyboard().IsVisibleInTray();
1116 }
1117 
IsEnterpriseIconVisibleForVirtualKeyboard()1118 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleForVirtualKeyboard() {
1119   return virtual_keyboard().IsEnterpriseIconVisible();
1120 }
1121 
ShowFloatingMenuIfEnabled()1122 void AccessibilityControllerImpl::ShowFloatingMenuIfEnabled() {
1123   if (floating_menu().enabled() && !floating_menu_controller_) {
1124     floating_menu_controller_ =
1125         std::make_unique<FloatingAccessibilityController>(this);
1126     floating_menu_controller_->Show(GetFloatingMenuPosition());
1127   } else {
1128     always_show_floating_menu_when_enabled_ = true;
1129   }
1130 }
1131 
1132 FloatingAccessibilityController*
GetFloatingMenuController()1133 AccessibilityControllerImpl::GetFloatingMenuController() {
1134   return floating_menu_controller_.get();
1135 }
1136 
GetPointScanController()1137 PointScanController* AccessibilityControllerImpl::GetPointScanController() {
1138   return point_scan_controller_.get();
1139 }
1140 
SetTabletModeShelfNavigationButtonsEnabled(bool enabled)1141 void AccessibilityControllerImpl::SetTabletModeShelfNavigationButtonsEnabled(
1142     bool enabled) {
1143   if (!active_user_prefs_)
1144     return;
1145 
1146   active_user_prefs_->SetBoolean(
1147       prefs::kAccessibilityTabletModeShelfNavigationButtonsEnabled, enabled);
1148   active_user_prefs_->CommitPendingWrite();
1149 }
1150 
TriggerAccessibilityAlert(AccessibilityAlert alert)1151 void AccessibilityControllerImpl::TriggerAccessibilityAlert(
1152     AccessibilityAlert alert) {
1153   if (client_)
1154     client_->TriggerAccessibilityAlert(alert);
1155 }
1156 
TriggerAccessibilityAlertWithMessage(const std::string & message)1157 void AccessibilityControllerImpl::TriggerAccessibilityAlertWithMessage(
1158     const std::string& message) {
1159   if (client_)
1160     client_->TriggerAccessibilityAlertWithMessage(message);
1161 }
1162 
PlayEarcon(int32_t sound_key)1163 void AccessibilityControllerImpl::PlayEarcon(int32_t sound_key) {
1164   if (client_)
1165     client_->PlayEarcon(sound_key);
1166 }
1167 
PlayShutdownSound()1168 base::TimeDelta AccessibilityControllerImpl::PlayShutdownSound() {
1169   return client_ ? client_->PlayShutdownSound() : base::TimeDelta();
1170 }
1171 
HandleAccessibilityGesture(ax::mojom::Gesture gesture,gfx::PointF location)1172 void AccessibilityControllerImpl::HandleAccessibilityGesture(
1173     ax::mojom::Gesture gesture,
1174     gfx::PointF location) {
1175   if (client_)
1176     client_->HandleAccessibilityGesture(gesture, location);
1177 }
1178 
ToggleDictation()1179 void AccessibilityControllerImpl::ToggleDictation() {
1180   // Do nothing if dictation is not enabled.
1181   if (!dictation().enabled())
1182     return;
1183 
1184   if (client_) {
1185     const bool is_active = client_->ToggleDictation();
1186     SetDictationActive(is_active);
1187     if (is_active)
1188       Shell::Get()->OnDictationStarted();
1189     else
1190       Shell::Get()->OnDictationEnded();
1191   }
1192 }
1193 
SetDictationActive(bool is_active)1194 void AccessibilityControllerImpl::SetDictationActive(bool is_active) {
1195   dictation_active_ = is_active;
1196 }
1197 
ToggleDictationFromSource(DictationToggleSource source)1198 void AccessibilityControllerImpl::ToggleDictationFromSource(
1199     DictationToggleSource source) {
1200   base::RecordAction(base::UserMetricsAction("Accel_Toggle_Dictation"));
1201   UserMetricsRecorder::RecordUserToggleDictation(source);
1202 
1203   dictation().SetEnabled(true);
1204   ToggleDictation();
1205 }
1206 
SilenceSpokenFeedback()1207 void AccessibilityControllerImpl::SilenceSpokenFeedback() {
1208   if (client_)
1209     client_->SilenceSpokenFeedback();
1210 }
1211 
OnTwoFingerTouchStart()1212 void AccessibilityControllerImpl::OnTwoFingerTouchStart() {
1213   if (client_)
1214     client_->OnTwoFingerTouchStart();
1215 }
1216 
OnTwoFingerTouchStop()1217 void AccessibilityControllerImpl::OnTwoFingerTouchStop() {
1218   if (client_)
1219     client_->OnTwoFingerTouchStop();
1220 }
1221 
ShouldToggleSpokenFeedbackViaTouch() const1222 bool AccessibilityControllerImpl::ShouldToggleSpokenFeedbackViaTouch() const {
1223   return client_ && client_->ShouldToggleSpokenFeedbackViaTouch();
1224 }
1225 
PlaySpokenFeedbackToggleCountdown(int tick_count)1226 void AccessibilityControllerImpl::PlaySpokenFeedbackToggleCountdown(
1227     int tick_count) {
1228   if (client_)
1229     client_->PlaySpokenFeedbackToggleCountdown(tick_count);
1230 }
1231 
IsEnterpriseIconVisibleInTrayMenu(const std::string & path)1232 bool AccessibilityControllerImpl::IsEnterpriseIconVisibleInTrayMenu(
1233     const std::string& path) {
1234   return active_user_prefs_ &&
1235          active_user_prefs_->FindPreference(path)->IsManaged();
1236 }
1237 
SetClient(AccessibilityControllerClient * client)1238 void AccessibilityControllerImpl::SetClient(
1239     AccessibilityControllerClient* client) {
1240   client_ = client;
1241 }
1242 
SetDarkenScreen(bool darken)1243 void AccessibilityControllerImpl::SetDarkenScreen(bool darken) {
1244   if (darken && !scoped_backlights_forced_off_) {
1245     scoped_backlights_forced_off_ =
1246         Shell::Get()->backlights_forced_off_setter()->ForceBacklightsOff();
1247   } else if (!darken && scoped_backlights_forced_off_) {
1248     scoped_backlights_forced_off_.reset();
1249   }
1250 }
1251 
BrailleDisplayStateChanged(bool connected)1252 void AccessibilityControllerImpl::BrailleDisplayStateChanged(bool connected) {
1253   A11yNotificationType type = A11yNotificationType::kNone;
1254   if (connected && spoken_feedback().enabled())
1255     type = A11yNotificationType::kBrailleDisplayConnected;
1256   else if (connected && !spoken_feedback().enabled())
1257     type = A11yNotificationType::kSpokenFeedbackBrailleEnabled;
1258 
1259   if (connected)
1260     SetSpokenFeedbackEnabled(true, A11Y_NOTIFICATION_NONE);
1261   NotifyAccessibilityStatusChanged();
1262 
1263   ShowAccessibilityNotification(type);
1264 }
1265 
SetFocusHighlightRect(const gfx::Rect & bounds_in_screen)1266 void AccessibilityControllerImpl::SetFocusHighlightRect(
1267     const gfx::Rect& bounds_in_screen) {
1268   if (!accessibility_highlight_controller_)
1269     return;
1270   accessibility_highlight_controller_->SetFocusHighlightRect(bounds_in_screen);
1271 }
1272 
SetCaretBounds(const gfx::Rect & bounds_in_screen)1273 void AccessibilityControllerImpl::SetCaretBounds(
1274     const gfx::Rect& bounds_in_screen) {
1275   if (!accessibility_highlight_controller_)
1276     return;
1277   accessibility_highlight_controller_->SetCaretBounds(bounds_in_screen);
1278 }
1279 
SetAccessibilityPanelAlwaysVisible(bool always_visible)1280 void AccessibilityControllerImpl::SetAccessibilityPanelAlwaysVisible(
1281     bool always_visible) {
1282   GetLayoutManager()->SetAlwaysVisible(always_visible);
1283 }
1284 
SetAccessibilityPanelBounds(const gfx::Rect & bounds,AccessibilityPanelState state)1285 void AccessibilityControllerImpl::SetAccessibilityPanelBounds(
1286     const gfx::Rect& bounds,
1287     AccessibilityPanelState state) {
1288   GetLayoutManager()->SetPanelBounds(bounds, state);
1289 }
1290 
OnSigninScreenPrefServiceInitialized(PrefService * prefs)1291 void AccessibilityControllerImpl::OnSigninScreenPrefServiceInitialized(
1292     PrefService* prefs) {
1293   // Make |kA11yPrefsForRecommendedValueOnSignin| observing recommended values
1294   // on signin screen. See PolicyRecommendationRestorer.
1295   PolicyRecommendationRestorer* policy_recommendation_restorer =
1296       Shell::Get()->policy_recommendation_restorer();
1297   for (auto* const pref_name : kA11yPrefsForRecommendedValueOnSignin)
1298     policy_recommendation_restorer->ObservePref(pref_name);
1299 
1300   // Observe user settings. This must happen after PolicyRecommendationRestorer.
1301   ObservePrefs(prefs);
1302 }
1303 
OnActiveUserPrefServiceChanged(PrefService * prefs)1304 void AccessibilityControllerImpl::OnActiveUserPrefServiceChanged(
1305     PrefService* prefs) {
1306   // This is guaranteed to be received after
1307   // OnSigninScreenPrefServiceInitialized() so only copy the signin prefs if
1308   // needed here.
1309   CopySigninPrefsIfNeeded(active_user_prefs_, prefs);
1310   ObservePrefs(prefs);
1311 }
1312 
1313 AccessibilityEventRewriter*
GetAccessibilityEventRewriterForTest()1314 AccessibilityControllerImpl::GetAccessibilityEventRewriterForTest() {
1315   return accessibility_event_rewriter_;
1316 }
1317 
1318 void AccessibilityControllerImpl::
DisableSwitchAccessDisableConfirmationDialogTesting()1319     DisableSwitchAccessDisableConfirmationDialogTesting() {
1320   no_switch_access_disable_confirmation_dialog_for_testing_ = true;
1321 }
1322 
OnTabletModeStarted()1323 void AccessibilityControllerImpl::OnTabletModeStarted() {
1324   if (spoken_feedback().enabled())
1325     ShowAccessibilityNotification(A11yNotificationType::kSpokenFeedbackEnabled);
1326 }
1327 
OnTabletModeEnded()1328 void AccessibilityControllerImpl::OnTabletModeEnded() {
1329   if (spoken_feedback().enabled())
1330     ShowAccessibilityNotification(A11yNotificationType::kSpokenFeedbackEnabled);
1331 }
1332 
ObservePrefs(PrefService * prefs)1333 void AccessibilityControllerImpl::ObservePrefs(PrefService* prefs) {
1334   DCHECK(prefs);
1335 
1336   active_user_prefs_ = prefs;
1337 
1338   // Watch for pref updates from webui settings and policy.
1339   pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
1340   pref_change_registrar_->Init(prefs);
1341 
1342   // It is safe to use base::Unreatined since we own pref_change_registrar.
1343   for (int feature_id = 0; feature_id < FeatureType::kFeatureCount;
1344        feature_id++) {
1345     Feature* feature = features_[feature_id].get();
1346     DCHECK(feature);
1347     pref_change_registrar_->Add(
1348         feature->pref_name(),
1349         base::BindRepeating(
1350             &AccessibilityControllerImpl::Feature::UpdateFromPref,
1351             base::Unretained(feature)));
1352     feature->UpdateFromPref();
1353   }
1354 
1355   pref_change_registrar_->Add(
1356       prefs::kAccessibilityAutoclickDelayMs,
1357       base::BindRepeating(
1358           &AccessibilityControllerImpl::UpdateAutoclickDelayFromPref,
1359           base::Unretained(this)));
1360   pref_change_registrar_->Add(
1361       prefs::kAccessibilityAutoclickEventType,
1362       base::BindRepeating(
1363           &AccessibilityControllerImpl::UpdateAutoclickEventTypeFromPref,
1364           base::Unretained(this)));
1365   pref_change_registrar_->Add(
1366       prefs::kAccessibilityAutoclickRevertToLeftClick,
1367       base::BindRepeating(&AccessibilityControllerImpl::
1368                               UpdateAutoclickRevertToLeftClickFromPref,
1369                           base::Unretained(this)));
1370   pref_change_registrar_->Add(
1371       prefs::kAccessibilityAutoclickStabilizePosition,
1372       base::BindRepeating(&AccessibilityControllerImpl::
1373                               UpdateAutoclickStabilizePositionFromPref,
1374                           base::Unretained(this)));
1375   pref_change_registrar_->Add(
1376       prefs::kAccessibilityAutoclickMovementThreshold,
1377       base::BindRepeating(&AccessibilityControllerImpl::
1378                               UpdateAutoclickMovementThresholdFromPref,
1379                           base::Unretained(this)));
1380   pref_change_registrar_->Add(
1381       prefs::kAccessibilityAutoclickMenuPosition,
1382       base::BindRepeating(
1383           &AccessibilityControllerImpl::UpdateAutoclickMenuPositionFromPref,
1384           base::Unretained(this)));
1385   pref_change_registrar_->Add(
1386       prefs::kAccessibilityFloatingMenuPosition,
1387       base::BindRepeating(
1388           &AccessibilityControllerImpl::UpdateFloatingMenuPositionFromPref,
1389           base::Unretained(this)));
1390   pref_change_registrar_->Add(
1391       prefs::kAccessibilityLargeCursorDipSize,
1392       base::BindRepeating(
1393           &AccessibilityControllerImpl::UpdateLargeCursorFromPref,
1394           base::Unretained(this)));
1395   pref_change_registrar_->Add(
1396       prefs::kAccessibilityShortcutsEnabled,
1397       base::BindRepeating(
1398           &AccessibilityControllerImpl::UpdateShortcutsEnabledFromPref,
1399           base::Unretained(this)));
1400   pref_change_registrar_->Add(
1401       prefs::kAccessibilitySwitchAccessSelectKeyCodes,
1402       base::BindRepeating(
1403           &AccessibilityControllerImpl::UpdateSwitchAccessKeyCodesFromPref,
1404           base::Unretained(this), SwitchAccessCommand::kSelect));
1405   pref_change_registrar_->Add(
1406       prefs::kAccessibilitySwitchAccessNextKeyCodes,
1407       base::BindRepeating(
1408           &AccessibilityControllerImpl::UpdateSwitchAccessKeyCodesFromPref,
1409           base::Unretained(this), SwitchAccessCommand::kNext));
1410   pref_change_registrar_->Add(
1411       prefs::kAccessibilitySwitchAccessPreviousKeyCodes,
1412       base::BindRepeating(
1413           &AccessibilityControllerImpl::UpdateSwitchAccessKeyCodesFromPref,
1414           base::Unretained(this), SwitchAccessCommand::kPrevious));
1415   pref_change_registrar_->Add(
1416       prefs::kAccessibilitySwitchAccessAutoScanEnabled,
1417       base::BindRepeating(&AccessibilityControllerImpl::
1418                               UpdateSwitchAccessAutoScanEnabledFromPref,
1419                           base::Unretained(this)));
1420   pref_change_registrar_->Add(
1421       prefs::kAccessibilitySwitchAccessAutoScanSpeedMs,
1422       base::BindRepeating(
1423           &AccessibilityControllerImpl::UpdateSwitchAccessAutoScanSpeedFromPref,
1424           base::Unretained(this)));
1425   pref_change_registrar_->Add(
1426       prefs::kAccessibilitySwitchAccessAutoScanKeyboardSpeedMs,
1427       base::BindRepeating(&AccessibilityControllerImpl::
1428                               UpdateSwitchAccessAutoScanKeyboardSpeedFromPref,
1429                           base::Unretained(this)));
1430   pref_change_registrar_->Add(
1431       prefs::kAccessibilitySwitchAccessNextSetting,
1432       base::BindRepeating(
1433           &AccessibilityControllerImpl::SyncSwitchAccessPrefsToSignInProfile,
1434           base::Unretained(this)));
1435   pref_change_registrar_->Add(
1436       prefs::kAccessibilitySwitchAccessPreviousSetting,
1437       base::BindRepeating(
1438           &AccessibilityControllerImpl::SyncSwitchAccessPrefsToSignInProfile,
1439           base::Unretained(this)));
1440   pref_change_registrar_->Add(
1441       prefs::kAccessibilitySwitchAccessSelectSetting,
1442       base::BindRepeating(
1443           &AccessibilityControllerImpl::SyncSwitchAccessPrefsToSignInProfile,
1444           base::Unretained(this)));
1445   pref_change_registrar_->Add(
1446       prefs::kAccessibilityTabletModeShelfNavigationButtonsEnabled,
1447       base::BindRepeating(&AccessibilityControllerImpl::
1448                               UpdateTabletModeShelfNavigationButtonsFromPref,
1449                           base::Unretained(this)));
1450   pref_change_registrar_->Add(
1451       prefs::kAccessibilityCursorColor,
1452       base::BindRepeating(
1453           &AccessibilityControllerImpl::UpdateCursorColorFromPrefs,
1454           base::Unretained(this)));
1455 
1456   // Load current state.
1457   for (int feature_id = 0; feature_id < FeatureType::kFeatureCount;
1458        feature_id++) {
1459     features_[feature_id]->UpdateFromPref();
1460   }
1461 
1462   UpdateAutoclickDelayFromPref();
1463   UpdateAutoclickEventTypeFromPref();
1464   UpdateAutoclickRevertToLeftClickFromPref();
1465   UpdateAutoclickStabilizePositionFromPref();
1466   UpdateAutoclickMovementThresholdFromPref();
1467   UpdateAutoclickMenuPositionFromPref();
1468   UpdateFloatingMenuPositionFromPref();
1469   UpdateLargeCursorFromPref();
1470   UpdateCursorColorFromPrefs();
1471   UpdateShortcutsEnabledFromPref();
1472   UpdateTabletModeShelfNavigationButtonsFromPref();
1473 }
1474 
UpdateAutoclickDelayFromPref()1475 void AccessibilityControllerImpl::UpdateAutoclickDelayFromPref() {
1476   DCHECK(active_user_prefs_);
1477   base::TimeDelta autoclick_delay = base::TimeDelta::FromMilliseconds(int64_t{
1478       active_user_prefs_->GetInteger(prefs::kAccessibilityAutoclickDelayMs)});
1479 
1480   if (autoclick_delay_ == autoclick_delay)
1481     return;
1482   autoclick_delay_ = autoclick_delay;
1483 
1484   Shell::Get()->autoclick_controller()->SetAutoclickDelay(autoclick_delay_);
1485 }
1486 
UpdateAutoclickEventTypeFromPref()1487 void AccessibilityControllerImpl::UpdateAutoclickEventTypeFromPref() {
1488   Shell::Get()->autoclick_controller()->SetAutoclickEventType(
1489       GetAutoclickEventType());
1490 }
1491 
SetAutoclickEventType(AutoclickEventType event_type)1492 void AccessibilityControllerImpl::SetAutoclickEventType(
1493     AutoclickEventType event_type) {
1494   if (!active_user_prefs_)
1495     return;
1496   active_user_prefs_->SetInteger(prefs::kAccessibilityAutoclickEventType,
1497                                  static_cast<int>(event_type));
1498   active_user_prefs_->CommitPendingWrite();
1499   Shell::Get()->autoclick_controller()->SetAutoclickEventType(event_type);
1500 }
1501 
GetAutoclickEventType()1502 AutoclickEventType AccessibilityControllerImpl::GetAutoclickEventType() {
1503   DCHECK(active_user_prefs_);
1504   return static_cast<AutoclickEventType>(
1505       active_user_prefs_->GetInteger(prefs::kAccessibilityAutoclickEventType));
1506 }
1507 
UpdateAutoclickRevertToLeftClickFromPref()1508 void AccessibilityControllerImpl::UpdateAutoclickRevertToLeftClickFromPref() {
1509   DCHECK(active_user_prefs_);
1510   bool revert_to_left_click = active_user_prefs_->GetBoolean(
1511       prefs::kAccessibilityAutoclickRevertToLeftClick);
1512 
1513   Shell::Get()->autoclick_controller()->set_revert_to_left_click(
1514       revert_to_left_click);
1515 }
1516 
UpdateAutoclickStabilizePositionFromPref()1517 void AccessibilityControllerImpl::UpdateAutoclickStabilizePositionFromPref() {
1518   DCHECK(active_user_prefs_);
1519   bool stabilize_position = active_user_prefs_->GetBoolean(
1520       prefs::kAccessibilityAutoclickStabilizePosition);
1521 
1522   Shell::Get()->autoclick_controller()->set_stabilize_click_position(
1523       stabilize_position);
1524 }
1525 
UpdateAutoclickMovementThresholdFromPref()1526 void AccessibilityControllerImpl::UpdateAutoclickMovementThresholdFromPref() {
1527   DCHECK(active_user_prefs_);
1528   int movement_threshold = active_user_prefs_->GetInteger(
1529       prefs::kAccessibilityAutoclickMovementThreshold);
1530 
1531   Shell::Get()->autoclick_controller()->SetMovementThreshold(
1532       movement_threshold);
1533 }
1534 
UpdateAutoclickMenuPositionFromPref()1535 void AccessibilityControllerImpl::UpdateAutoclickMenuPositionFromPref() {
1536   Shell::Get()->autoclick_controller()->SetMenuPosition(
1537       GetAutoclickMenuPosition());
1538 }
1539 
SetAutoclickMenuPosition(FloatingMenuPosition position)1540 void AccessibilityControllerImpl::SetAutoclickMenuPosition(
1541     FloatingMenuPosition position) {
1542   if (!active_user_prefs_)
1543     return;
1544   active_user_prefs_->SetInteger(prefs::kAccessibilityAutoclickMenuPosition,
1545                                  static_cast<int>(position));
1546   active_user_prefs_->CommitPendingWrite();
1547   Shell::Get()->autoclick_controller()->SetMenuPosition(position);
1548 }
1549 
GetAutoclickMenuPosition()1550 FloatingMenuPosition AccessibilityControllerImpl::GetAutoclickMenuPosition() {
1551   DCHECK(active_user_prefs_);
1552   return static_cast<FloatingMenuPosition>(active_user_prefs_->GetInteger(
1553       prefs::kAccessibilityAutoclickMenuPosition));
1554 }
1555 
RequestAutoclickScrollableBoundsForPoint(gfx::Point & point_in_screen)1556 void AccessibilityControllerImpl::RequestAutoclickScrollableBoundsForPoint(
1557     gfx::Point& point_in_screen) {
1558   if (client_)
1559     client_->RequestAutoclickScrollableBoundsForPoint(point_in_screen);
1560 }
1561 
MagnifierBoundsChanged(const gfx::Rect & bounds_in_screen)1562 void AccessibilityControllerImpl::MagnifierBoundsChanged(
1563     const gfx::Rect& bounds_in_screen) {
1564   if (client_)
1565     client_->MagnifierBoundsChanged(bounds_in_screen);
1566 }
1567 
UpdateAutoclickMenuBoundsIfNeeded()1568 void AccessibilityControllerImpl::UpdateAutoclickMenuBoundsIfNeeded() {
1569   Shell::Get()->autoclick_controller()->UpdateAutoclickMenuBoundsIfNeeded();
1570 }
1571 
HandleAutoclickScrollableBoundsFound(gfx::Rect & bounds_in_screen)1572 void AccessibilityControllerImpl::HandleAutoclickScrollableBoundsFound(
1573     gfx::Rect& bounds_in_screen) {
1574   Shell::Get()->autoclick_controller()->HandleAutoclickScrollableBoundsFound(
1575       bounds_in_screen);
1576 }
1577 
SetFloatingMenuPosition(FloatingMenuPosition position)1578 void AccessibilityControllerImpl::SetFloatingMenuPosition(
1579     FloatingMenuPosition position) {
1580   if (!active_user_prefs_)
1581     return;
1582   active_user_prefs_->SetInteger(prefs::kAccessibilityFloatingMenuPosition,
1583                                  static_cast<int>(position));
1584   active_user_prefs_->CommitPendingWrite();
1585 }
1586 
UpdateFloatingMenuPositionFromPref()1587 void AccessibilityControllerImpl::UpdateFloatingMenuPositionFromPref() {
1588   if (floating_menu_controller_)
1589     floating_menu_controller_->SetMenuPosition(GetFloatingMenuPosition());
1590 }
1591 
GetFloatingMenuPosition()1592 FloatingMenuPosition AccessibilityControllerImpl::GetFloatingMenuPosition() {
1593   DCHECK(active_user_prefs_);
1594   return static_cast<FloatingMenuPosition>(active_user_prefs_->GetInteger(
1595       prefs::kAccessibilityFloatingMenuPosition));
1596 }
1597 
UpdateLargeCursorFromPref()1598 void AccessibilityControllerImpl::UpdateLargeCursorFromPref() {
1599   DCHECK(active_user_prefs_);
1600   const bool enabled =
1601       active_user_prefs_->GetBoolean(prefs::kAccessibilityLargeCursorEnabled);
1602   // Reset large cursor size to the default size when large cursor is disabled.
1603   if (!enabled)
1604     active_user_prefs_->ClearPref(prefs::kAccessibilityLargeCursorDipSize);
1605   const int size =
1606       active_user_prefs_->GetInteger(prefs::kAccessibilityLargeCursorDipSize);
1607 
1608   if (large_cursor_size_in_dip_ == size)
1609     return;
1610 
1611   large_cursor_size_in_dip_ = size;
1612 
1613   NotifyAccessibilityStatusChanged();
1614 
1615   Shell* shell = Shell::Get();
1616   shell->cursor_manager()->SetCursorSize(large_cursor().enabled()
1617                                              ? ui::CursorSize::kLarge
1618                                              : ui::CursorSize::kNormal);
1619   shell->SetLargeCursorSizeInDip(large_cursor_size_in_dip_);
1620   shell->UpdateCursorCompositingEnabled();
1621 }
1622 
UpdateCursorColorFromPrefs()1623 void AccessibilityControllerImpl::UpdateCursorColorFromPrefs() {
1624   DCHECK(active_user_prefs_);
1625 
1626   // Not yet released: cursor color is behind a flag.
1627   if (!features::IsAccessibilityCursorColorEnabled())
1628     return;
1629 
1630   const bool enabled =
1631       active_user_prefs_->GetBoolean(prefs::kAccessibilityCursorColorEnabled);
1632   Shell* shell = Shell::Get();
1633   shell->SetCursorColor(
1634       enabled ? active_user_prefs_->GetInteger(prefs::kAccessibilityCursorColor)
1635               : kDefaultCursorColor);
1636   NotifyAccessibilityStatusChanged();
1637   shell->UpdateCursorCompositingEnabled();
1638 }
1639 
UpdateAccessibilityHighlightingFromPrefs()1640 void AccessibilityControllerImpl::UpdateAccessibilityHighlightingFromPrefs() {
1641   if (!caret_highlight().enabled() && !cursor_highlight().enabled() &&
1642       !focus_highlight().enabled()) {
1643     accessibility_highlight_controller_.reset();
1644     return;
1645   }
1646 
1647   if (!accessibility_highlight_controller_) {
1648     accessibility_highlight_controller_ =
1649         std::make_unique<AccessibilityHighlightController>();
1650   }
1651 
1652   accessibility_highlight_controller_->HighlightCaret(
1653       caret_highlight().enabled());
1654   accessibility_highlight_controller_->HighlightCursor(
1655       cursor_highlight().enabled());
1656   accessibility_highlight_controller_->HighlightFocus(
1657       focus_highlight().enabled());
1658 }
1659 
MaybeCreateSelectToSpeakEventHandler()1660 void AccessibilityControllerImpl::MaybeCreateSelectToSpeakEventHandler() {
1661   // Construct the handler as needed when Select-to-Speak is enabled and the
1662   // delegate is set. Otherwise, destroy the handler when Select-to-Speak is
1663   // disabled or the delegate has been destroyed.
1664   if (!select_to_speak().enabled() ||
1665       !select_to_speak_event_handler_delegate_) {
1666     select_to_speak_event_handler_.reset();
1667     return;
1668   }
1669 
1670   if (select_to_speak_event_handler_)
1671     return;
1672 
1673   select_to_speak_event_handler_ = std::make_unique<SelectToSpeakEventHandler>(
1674       select_to_speak_event_handler_delegate_);
1675 }
1676 
UpdateSwitchAccessKeyCodesFromPref(SwitchAccessCommand command)1677 void AccessibilityControllerImpl::UpdateSwitchAccessKeyCodesFromPref(
1678     SwitchAccessCommand command) {
1679   if (!active_user_prefs_)
1680     return;
1681 
1682   SyncSwitchAccessPrefsToSignInProfile();
1683 
1684   std::string pref_key = PrefKeyForSwitchAccessCommand(command);
1685   const base::ListValue* key_codes_pref = active_user_prefs_->GetList(pref_key);
1686   std::set<int> key_codes;
1687   for (const base::Value& v : *key_codes_pref) {
1688     int key_code = v.GetInt();
1689     key_codes.insert(key_code);
1690   }
1691 
1692   std::string uma_name = UmaNameForSwitchAccessCommand(command);
1693   if (key_codes.size() == 0) {
1694     SwitchAccessCommandKeyCode uma_value = UmaValueForKeyCode(0);
1695     base::UmaHistogramEnumeration(uma_name, uma_value);
1696   }
1697   for (int key_code : key_codes) {
1698     SwitchAccessCommandKeyCode uma_value = UmaValueForKeyCode(key_code);
1699     base::UmaHistogramEnumeration(uma_name, uma_value);
1700   }
1701 
1702   if (accessibility_event_rewriter_)
1703     accessibility_event_rewriter_->SetKeyCodesForSwitchAccessCommand(key_codes,
1704                                                                      command);
1705 }
1706 
UpdateSwitchAccessAutoScanEnabledFromPref()1707 void AccessibilityControllerImpl::UpdateSwitchAccessAutoScanEnabledFromPref() {
1708   DCHECK(active_user_prefs_);
1709   const bool enabled = active_user_prefs_->GetBoolean(
1710       prefs::kAccessibilitySwitchAccessAutoScanEnabled);
1711 
1712   base::UmaHistogramBoolean("Accessibility.CrosSwitchAccess.AutoScan", enabled);
1713   SyncSwitchAccessPrefsToSignInProfile();
1714 }
1715 
UpdateSwitchAccessAutoScanSpeedFromPref()1716 void AccessibilityControllerImpl::UpdateSwitchAccessAutoScanSpeedFromPref() {
1717   DCHECK(active_user_prefs_);
1718   const int speed_ms = active_user_prefs_->GetInteger(
1719       prefs::kAccessibilitySwitchAccessAutoScanSpeedMs);
1720 
1721   base::UmaHistogramCustomCounts(
1722       "Accessibility.CrosSwitchAccess.AutoScan.SpeedMs", speed_ms, 1 /* min */,
1723       10000 /* max */, 100 /* buckets */);
1724   SyncSwitchAccessPrefsToSignInProfile();
1725 }
1726 
1727 void AccessibilityControllerImpl::
UpdateSwitchAccessAutoScanKeyboardSpeedFromPref()1728     UpdateSwitchAccessAutoScanKeyboardSpeedFromPref() {
1729   DCHECK(active_user_prefs_);
1730   const int speed_ms = active_user_prefs_->GetInteger(
1731       prefs::kAccessibilitySwitchAccessAutoScanKeyboardSpeedMs);
1732 
1733   base::UmaHistogramCustomCounts(
1734       "Accessibility.CrosSwitchAccess.AutoScan.KeyboardSpeedMs", speed_ms,
1735       1 /* min */, 10000 /* max */, 100 /* buckets */);
1736   SyncSwitchAccessPrefsToSignInProfile();
1737 }
1738 
SwitchAccessDisableDialogClosed(bool disable_dialog_accepted)1739 void AccessibilityControllerImpl::SwitchAccessDisableDialogClosed(
1740     bool disable_dialog_accepted) {
1741   switch_access_disable_dialog_showing_ = false;
1742   // Always deactivate switch access. Turning switch access off ensures it is
1743   // re-activated correctly.
1744   // The pref was already disabled, but we left switch access on so the user
1745   // could interact with the dialog.
1746   DeactivateSwitchAccess();
1747   if (disable_dialog_accepted) {
1748     NotifyAccessibilityStatusChanged();
1749     SyncSwitchAccessPrefsToSignInProfile();
1750   } else {
1751     // Reset the preference (which was already set to false). Doing so turns
1752     // switch access back on.
1753     skip_switch_access_notification_ = true;
1754     switch_access().SetEnabled(true);
1755   }
1756 }
1757 
UpdateKeyCodesAfterSwitchAccessEnabled()1758 void AccessibilityControllerImpl::UpdateKeyCodesAfterSwitchAccessEnabled() {
1759   UpdateSwitchAccessKeyCodesFromPref(SwitchAccessCommand::kSelect);
1760   UpdateSwitchAccessKeyCodesFromPref(SwitchAccessCommand::kNext);
1761   UpdateSwitchAccessKeyCodesFromPref(SwitchAccessCommand::kPrevious);
1762 }
1763 
ActivateSwitchAccess()1764 void AccessibilityControllerImpl::ActivateSwitchAccess() {
1765   switch_access_bubble_controller_ =
1766       std::make_unique<SwitchAccessMenuBubbleController>();
1767   UpdateKeyCodesAfterSwitchAccessEnabled();
1768   if (skip_switch_access_notification_) {
1769     skip_switch_access_notification_ = false;
1770     return;
1771   }
1772 
1773   ShowAccessibilityNotification(A11yNotificationType::kSwitchAccessEnabled);
1774 }
1775 
DeactivateSwitchAccess()1776 void AccessibilityControllerImpl::DeactivateSwitchAccess() {
1777   if (client_)
1778     client_->OnSwitchAccessDisabled();
1779   switch_access_bubble_controller_.reset();
1780 }
1781 
SyncSwitchAccessPrefsToSignInProfile()1782 void AccessibilityControllerImpl::SyncSwitchAccessPrefsToSignInProfile() {
1783   if (!active_user_prefs_ || IsSigninPrefService(active_user_prefs_))
1784     return;
1785 
1786   PrefService* signin_prefs =
1787       Shell::Get()->session_controller()->GetSigninScreenPrefService();
1788   DCHECK(signin_prefs);
1789 
1790   for (const auto* pref_path : kSwitchAccessPrefsCopiedToSignin) {
1791     const PrefService::Preference* pref =
1792         active_user_prefs_->FindPreference(pref_path);
1793 
1794     // Ignore if the pref has not been set by the user.
1795     if (!pref || !pref->IsUserControlled())
1796       continue;
1797 
1798     // Copy the pref value to the signin profile.
1799     const base::Value* value = pref->GetValue();
1800     signin_prefs->Set(pref_path, *value);
1801   }
1802 }
1803 
UpdateShortcutsEnabledFromPref()1804 void AccessibilityControllerImpl::UpdateShortcutsEnabledFromPref() {
1805   DCHECK(active_user_prefs_);
1806   const bool enabled =
1807       active_user_prefs_->GetBoolean(prefs::kAccessibilityShortcutsEnabled);
1808 
1809   if (shortcuts_enabled_ == enabled)
1810     return;
1811 
1812   shortcuts_enabled_ = enabled;
1813 
1814   NotifyAccessibilityStatusChanged();
1815 }
1816 
1817 void AccessibilityControllerImpl::
UpdateTabletModeShelfNavigationButtonsFromPref()1818     UpdateTabletModeShelfNavigationButtonsFromPref() {
1819   DCHECK(active_user_prefs_);
1820   const bool enabled = active_user_prefs_->GetBoolean(
1821       prefs::kAccessibilityTabletModeShelfNavigationButtonsEnabled);
1822 
1823   if (tablet_mode_shelf_navigation_buttons_enabled_ == enabled)
1824     return;
1825 
1826   tablet_mode_shelf_navigation_buttons_enabled_ = enabled;
1827 
1828   NotifyAccessibilityStatusChanged();
1829 }
1830 
GetBatteryDescription() const1831 base::string16 AccessibilityControllerImpl::GetBatteryDescription() const {
1832   // Pass battery status as string to callback function.
1833   return PowerStatus::Get()->GetAccessibleNameString(
1834       /*full_description=*/true);
1835 }
1836 
SetVirtualKeyboardVisible(bool is_visible)1837 void AccessibilityControllerImpl::SetVirtualKeyboardVisible(bool is_visible) {
1838   if (is_visible)
1839     Shell::Get()->keyboard_controller()->ShowKeyboard();
1840   else
1841     Shell::Get()->keyboard_controller()->HideKeyboard(HideReason::kUser);
1842 }
1843 
PerformAcceleratorAction(AcceleratorAction accelerator_action)1844 void AccessibilityControllerImpl::PerformAcceleratorAction(
1845     AcceleratorAction accelerator_action) {
1846   AcceleratorController::Get()->PerformActionIfEnabled(accelerator_action,
1847                                                        /* accelerator = */ {});
1848 }
1849 
NotifyAccessibilityStatusChanged()1850 void AccessibilityControllerImpl::NotifyAccessibilityStatusChanged() {
1851   for (auto& observer : observers_)
1852     observer.OnAccessibilityStatusChanged();
1853 }
1854 
IsAccessibilityFeatureVisibleInTrayMenu(const std::string & path)1855 bool AccessibilityControllerImpl::IsAccessibilityFeatureVisibleInTrayMenu(
1856     const std::string& path) {
1857   if (!active_user_prefs_)
1858     return true;
1859   if (active_user_prefs_->FindPreference(path)->IsManaged() &&
1860       !active_user_prefs_->GetBoolean(path)) {
1861     return false;
1862   }
1863   return true;
1864 }
1865 
UpdateFeatureFromPref(FeatureType feature)1866 void AccessibilityControllerImpl::UpdateFeatureFromPref(FeatureType feature) {
1867   bool enabled = features_[feature]->enabled();
1868 
1869   switch (feature) {
1870     case FeatureType::kAutoclick:
1871       Shell::Get()->autoclick_controller()->SetEnabled(
1872           enabled, true /* show confirmation dialog */);
1873       break;
1874     case FeatureType::kCaretHighlight:
1875       UpdateAccessibilityHighlightingFromPrefs();
1876       break;
1877     case FeatureType::KCursorHighlight:
1878       UpdateAccessibilityHighlightingFromPrefs();
1879       break;
1880     case FeatureType::kDictation:
1881       break;
1882     case FeatureType::kFloatingMenu:
1883       if (enabled && always_show_floating_menu_when_enabled_)
1884         ShowFloatingMenuIfEnabled();
1885       else
1886         floating_menu_controller_.reset();
1887       break;
1888     case FeatureType::kFocusHighlight:
1889       UpdateAccessibilityHighlightingFromPrefs();
1890       break;
1891     case FeatureType::kFullscreenMagnifier:
1892       break;
1893     case FeatureType::kDockedMagnifier:
1894       break;
1895     case FeatureType::kHighContrast:
1896       Shell::Get()->high_contrast_controller()->SetEnabled(enabled);
1897       Shell::Get()->UpdateCursorCompositingEnabled();
1898       break;
1899     case FeatureType::kLargeCursor:
1900       if (!enabled)
1901         active_user_prefs_->ClearPref(prefs::kAccessibilityLargeCursorDipSize);
1902 
1903       Shell::Get()->cursor_manager()->SetCursorSize(
1904           large_cursor().enabled() ? ui::CursorSize::kLarge
1905                                    : ui::CursorSize::kNormal);
1906       Shell::Get()->SetLargeCursorSizeInDip(large_cursor_size_in_dip_);
1907       Shell::Get()->UpdateCursorCompositingEnabled();
1908       break;
1909     case FeatureType::kMonoAudio:
1910       chromeos::CrasAudioHandler::Get()->SetOutputMonoEnabled(enabled);
1911       break;
1912     case FeatureType::kSpokenFeedback:
1913       message_center::MessageCenter::Get()->SetSpokenFeedbackEnabled(enabled);
1914       // TODO(warx): ChromeVox loading/unloading requires browser process
1915       // started, thus it is still handled on Chrome side.
1916 
1917       // ChromeVox focus highlighting overrides the other focus highlighting.
1918       focus_highlight().UpdateFromPref();
1919       break;
1920     case FeatureType::kSelectToSpeak:
1921       select_to_speak_state_ = SelectToSpeakState::kSelectToSpeakStateInactive;
1922       if (enabled)
1923         MaybeCreateSelectToSpeakEventHandler();
1924       else
1925         select_to_speak_event_handler_.reset();
1926       break;
1927     case FeatureType::kStickyKeys:
1928       Shell::Get()->sticky_keys_controller()->Enable(enabled);
1929       break;
1930     case FeatureType::kSwitchAccess:
1931       if (!enabled) {
1932         ShowAccessibilityNotification(A11yNotificationType::kNone);
1933         if (no_switch_access_disable_confirmation_dialog_for_testing_) {
1934           SwitchAccessDisableDialogClosed(true);
1935         } else {
1936           // Show a dialog before disabling Switch Access.
1937           new AccessibilityFeatureDisableDialog(
1938               IDS_ASH_SWITCH_ACCESS_DISABLE_CONFIRMATION_TEXT,
1939               base::BindOnce(
1940                   &AccessibilityControllerImpl::SwitchAccessDisableDialogClosed,
1941                   weak_ptr_factory_.GetWeakPtr(), true),
1942               base::BindOnce(
1943                   &AccessibilityControllerImpl::SwitchAccessDisableDialogClosed,
1944                   weak_ptr_factory_.GetWeakPtr(), false));
1945           switch_access_disable_dialog_showing_ = true;
1946         }
1947         // Return early. We will call NotifyAccessibilityStatusChanged() if the
1948         // user accepts the dialog.
1949         return;
1950       } else {
1951         ActivateSwitchAccess();
1952       }
1953       SyncSwitchAccessPrefsToSignInProfile();
1954       break;
1955     case FeatureType::kVirtualKeyboard:
1956       keyboard::SetAccessibilityKeyboardEnabled(enabled);
1957       break;
1958     case FeatureType::kCursorColor:
1959       UpdateCursorColorFromPrefs();
1960       break;
1961     case FeatureType::kFeatureCount:
1962     case FeatureType::kNoConflictingFeature:
1963       NOTREACHED();
1964   }
1965   NotifyAccessibilityStatusChanged();
1966 }
1967 
1968 }  // namespace ash
1969