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