1 // Copyright (c) 2013 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 "chrome/browser/chromeos/accessibility/accessibility_manager.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <memory>
11 #include <utility>
12 #include <vector>
13 
14 #include "ash/public/cpp/accelerators.h"
15 #include "ash/public/cpp/accessibility_controller.h"
16 #include "ash/public/cpp/accessibility_controller_enums.h"
17 #include "ash/public/cpp/accessibility_focus_ring_controller.h"
18 #include "ash/public/cpp/accessibility_focus_ring_info.h"
19 #include "ash/public/cpp/ash_pref_names.h"
20 #include "ash/root_window_controller.h"
21 #include "ash/shell.h"
22 #include "ash/sticky_keys/sticky_keys_controller.h"
23 #include "base/bind.h"
24 #include "base/callback.h"
25 #include "base/callback_helpers.h"
26 #include "base/command_line.h"
27 #include "base/memory/ptr_util.h"
28 #include "base/memory/singleton.h"
29 #include "base/metrics/histogram_functions.h"
30 #include "base/path_service.h"
31 #include "base/strings/string_piece.h"
32 #include "base/strings/string_split.h"
33 #include "base/strings/string_util.h"
34 #include "base/strings/stringprintf.h"
35 #include "base/values.h"
36 #include "chrome/browser/accessibility/accessibility_extension_api.h"
37 #include "chrome/browser/browser_process.h"
38 #include "chrome/browser/chrome_notification_types.h"
39 #include "chrome/browser/chromeos/accessibility/accessibility_extension_loader.h"
40 #include "chrome/browser/chromeos/accessibility/dictation_chromeos.h"
41 #include "chrome/browser/chromeos/accessibility/magnification_manager.h"
42 #include "chrome/browser/chromeos/accessibility/select_to_speak_event_handler_delegate.h"
43 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
44 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
45 #include "chrome/browser/chromeos/profiles/profile_helper.h"
46 #include "chrome/browser/extensions/api/braille_display_private/stub_braille_controller.h"
47 #include "chrome/browser/extensions/extension_service.h"
48 #include "chrome/browser/profiles/profile_manager.h"
49 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
50 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
51 #include "chrome/browser/ui/singleton_tabs.h"
52 #include "chrome/common/chrome_paths.h"
53 #include "chrome/common/extensions/api/accessibility_private.h"
54 #include "chrome/common/extensions/extension_constants.h"
55 #include "chrome/common/pref_names.h"
56 #include "chrome/common/url_constants.h"
57 #include "chrome/grit/browser_resources.h"
58 #include "chromeos/audio/chromeos_sounds.h"
59 #include "chromeos/constants/chromeos_switches.h"
60 #include "chromeos/dbus/power/power_manager_client.h"
61 #include "chromeos/dbus/upstart/upstart_client.h"
62 #include "components/language/core/browser/pref_names.h"
63 #include "components/prefs/pref_member.h"
64 #include "components/prefs/pref_service.h"
65 #include "components/user_manager/known_user.h"
66 #include "content/public/browser/browser_accessibility_state.h"
67 #include "content/public/browser/browser_task_traits.h"
68 #include "content/public/browser/browser_thread.h"
69 #include "content/public/browser/focused_node_details.h"
70 #include "content/public/browser/media_session_service.h"
71 #include "content/public/browser/notification_details.h"
72 #include "content/public/browser/notification_service.h"
73 #include "content/public/browser/notification_source.h"
74 #include "content/public/browser/tts_controller.h"
75 #include "content/public/browser/web_ui.h"
76 #include "content/public/common/content_switches.h"
77 #include "extensions/browser/api/virtual_keyboard_private/virtual_keyboard_delegate.h"
78 #include "extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api.h"
79 #include "extensions/common/constants.h"
80 #include "extensions/common/extension.h"
81 #include "extensions/common/extension_messages.h"
82 #include "extensions/common/extension_resource.h"
83 #include "extensions/common/host_id.h"
84 #include "services/audio/public/cpp/sounds/sounds_manager.h"
85 #include "ui/accessibility/accessibility_switches.h"
86 #include "ui/accessibility/ax_enum_util.h"
87 #include "ui/accessibility/ax_enums.mojom.h"
88 #include "ui/base/ime/chromeos/extension_ime_util.h"
89 #include "ui/base/resource/resource_bundle.h"
90 #include "ui/views/widget/widget.h"
91 #include "ui/views/widget/widget_observer.h"
92 #include "url/gurl.h"
93 
94 using extensions::api::braille_display_private::BrailleController;
95 using extensions::api::braille_display_private::DisplayState;
96 using extensions::api::braille_display_private::KeyEvent;
97 using extensions::api::braille_display_private::StubBrailleController;
98 
99 namespace chromeos {
100 
101 namespace {
102 
103 // When this flag is set, system sounds will not be played.
104 constexpr char kAshDisableSystemSounds[] = "ash-disable-system-sounds";
105 
106 // A key for the spoken feedback enabled boolean state for a known user.
107 const char kUserSpokenFeedbackEnabled[] = "UserSpokenFeedbackEnabled";
108 
109 // A key for the startup sound enabled boolean state for a known user.
110 const char kUserStartupSoundEnabled[] = "UserStartupSoundEnabled";
111 
112 // A key for the bluetooth braille display for a user.
113 const char kUserBluetoothBrailleDisplayAddress[] =
114     "UserBluetoothBrailleDisplayAddress";
115 
116 // The name of the Brltty upstart job.
117 constexpr char kBrlttyUpstartJobName[] = "brltty";
118 
119 static chromeos::AccessibilityManager* g_accessibility_manager = nullptr;
120 
121 static BrailleController* g_braille_controller_for_test = nullptr;
122 
GetBrailleController()123 BrailleController* GetBrailleController() {
124   if (g_braille_controller_for_test)
125     return g_braille_controller_for_test;
126   // Don't use the real braille controller for tests to avoid automatically
127   // starting ChromeVox which confuses some tests.
128   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
129   if (command_line->HasSwitch(::switches::kTestType))
130     return StubBrailleController::GetInstance();
131   return BrailleController::GetInstance();
132 }
133 
134 // Restarts (stops, then starts brltty). If |address| is empty, only stops.
135 // In Upstart, sending an explicit restart command is a no-op if the job isn't
136 // already started. Without knowledge regarding brltty's current job status,
137 // stop followed by start ensures we both stop a started job, and also start
138 // brltty.
RestartBrltty(const std::string & address)139 void RestartBrltty(const std::string& address) {
140   chromeos::UpstartClient* client = chromeos::UpstartClient::Get();
141   client->StopJob(kBrlttyUpstartJobName, {}, base::DoNothing());
142 
143   std::vector<std::string> args;
144   if (address.empty())
145     return;
146 
147   args.push_back(base::StringPrintf("ADDRESS=%s", address.c_str()));
148   client->StartJob(kBrlttyUpstartJobName, args, base::DoNothing());
149 }
150 
VolumeAdjustSoundEnabled()151 bool VolumeAdjustSoundEnabled() {
152   return !base::CommandLine::ForCurrentProcess()->HasSwitch(
153       chromeos::switches::kDisableVolumeAdjustSound);
154 }
155 
156 }  // namespace
157 
158 class AccessibilityPanelWidgetObserver : public views::WidgetObserver {
159  public:
AccessibilityPanelWidgetObserver(views::Widget * widget,base::OnceCallback<void ()> on_destroying)160   AccessibilityPanelWidgetObserver(views::Widget* widget,
161                                    base::OnceCallback<void()> on_destroying)
162       : widget_(widget), on_destroying_(std::move(on_destroying)) {
163     widget_->AddObserver(this);
164   }
165 
~AccessibilityPanelWidgetObserver()166   ~AccessibilityPanelWidgetObserver() override { CHECK(!IsInObserverList()); }
167 
OnWidgetClosing(views::Widget * widget)168   void OnWidgetClosing(views::Widget* widget) override {
169     CHECK_EQ(widget_, widget);
170     widget->RemoveObserver(this);
171     std::move(on_destroying_).Run();
172     // |this| should be deleted.
173   }
174 
OnWidgetDestroying(views::Widget * widget)175   void OnWidgetDestroying(views::Widget* widget) override {
176     CHECK_EQ(widget_, widget);
177     widget->RemoveObserver(this);
178     std::move(on_destroying_).Run();
179     // |this| should be deleted.
180   }
181 
182  private:
183   views::Widget* widget_;
184 
185   base::OnceCallback<void()> on_destroying_;
186 
187   DISALLOW_COPY_AND_ASSIGN(AccessibilityPanelWidgetObserver);
188 };
189 
190 ///////////////////////////////////////////////////////////////////////////////
191 // AccessibilityStatusEventDetails
192 
AccessibilityStatusEventDetails(AccessibilityNotificationType notification_type,bool enabled)193 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
194     AccessibilityNotificationType notification_type,
195     bool enabled)
196     : notification_type(notification_type), enabled(enabled) {}
197 
198 ///////////////////////////////////////////////////////////////////////////////
199 //
200 // AccessibilityManager
201 
202 // static
Initialize()203 void AccessibilityManager::Initialize() {
204   CHECK(g_accessibility_manager == NULL);
205   g_accessibility_manager = new AccessibilityManager();
206 }
207 
208 // static
Shutdown()209 void AccessibilityManager::Shutdown() {
210   CHECK(g_accessibility_manager);
211   delete g_accessibility_manager;
212   g_accessibility_manager = NULL;
213 }
214 
215 // static
Get()216 AccessibilityManager* AccessibilityManager::Get() {
217   return g_accessibility_manager;
218 }
219 
220 // static
ShowAccessibilityHelp(Browser * browser)221 void AccessibilityManager::ShowAccessibilityHelp(Browser* browser) {
222   ShowSingletonTab(browser, GURL(chrome::kChromeAccessibilityHelpURL));
223 }
224 
AccessibilityManager()225 AccessibilityManager::AccessibilityManager() {
226   notification_registrar_.Add(this,
227                               chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
228                               content::NotificationService::AllSources());
229   notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
230                               content::NotificationService::AllSources());
231   notification_registrar_.Add(this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
232                               content::NotificationService::AllSources());
233   input_method::InputMethodManager::Get()->AddObserver(this);
234   user_manager::UserManager::Get()->AddSessionStateObserver(this);
235 
236   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
237   audio::SoundsManager* manager = audio::SoundsManager::Get();
238   manager->Initialize(SOUND_SHUTDOWN,
239                       bundle.GetRawDataResource(IDR_SOUND_SHUTDOWN_WAV));
240   manager->Initialize(
241       SOUND_SPOKEN_FEEDBACK_ENABLED,
242       bundle.GetRawDataResource(IDR_SOUND_SPOKEN_FEEDBACK_ENABLED_WAV));
243   manager->Initialize(
244       SOUND_SPOKEN_FEEDBACK_DISABLED,
245       bundle.GetRawDataResource(IDR_SOUND_SPOKEN_FEEDBACK_DISABLED_WAV));
246   manager->Initialize(SOUND_PASSTHROUGH,
247                       bundle.GetRawDataResource(IDR_SOUND_PASSTHROUGH_WAV));
248   manager->Initialize(SOUND_EXIT_SCREEN,
249                       bundle.GetRawDataResource(IDR_SOUND_EXIT_SCREEN_WAV));
250   manager->Initialize(SOUND_ENTER_SCREEN,
251                       bundle.GetRawDataResource(IDR_SOUND_ENTER_SCREEN_WAV));
252   manager->Initialize(SOUND_SPOKEN_FEEDBACK_TOGGLE_COUNTDOWN_HIGH,
253                       bundle.GetRawDataResource(
254                           IDR_SOUND_SPOKEN_FEEDBACK_TOGGLE_COUNTDOWN_HIGH_WAV));
255   manager->Initialize(SOUND_SPOKEN_FEEDBACK_TOGGLE_COUNTDOWN_LOW,
256                       bundle.GetRawDataResource(
257                           IDR_SOUND_SPOKEN_FEEDBACK_TOGGLE_COUNTDOWN_LOW_WAV));
258   manager->Initialize(SOUND_TOUCH_TYPE,
259                       bundle.GetRawDataResource(IDR_SOUND_TOUCH_TYPE_WAV));
260   manager->Initialize(SOUND_DICTATION_END,
261                       bundle.GetRawDataResource(IDR_SOUND_DICTATION_END_WAV));
262   manager->Initialize(SOUND_DICTATION_START,
263                       bundle.GetRawDataResource(IDR_SOUND_DICTATION_START_WAV));
264   manager->Initialize(
265       SOUND_DICTATION_CANCEL,
266       bundle.GetRawDataResource(IDR_SOUND_DICTATION_CANCEL_WAV));
267   manager->Initialize(SOUND_STARTUP,
268                       bundle.GetRawDataResource(IDR_SOUND_STARTUP_WAV));
269 
270   if (VolumeAdjustSoundEnabled()) {
271     manager->Initialize(chromeos::SOUND_VOLUME_ADJUST,
272                         bundle.GetRawDataResource(IDR_SOUND_VOLUME_ADJUST_WAV));
273   }
274 
275   base::FilePath resources_path;
276   if (!base::PathService::Get(chrome::DIR_RESOURCES, &resources_path))
277     NOTREACHED();
278   accessibility_common_extension_loader_ =
279       base::WrapUnique(new AccessibilityExtensionLoader(
280           extension_misc::kAccessibilityCommonExtensionId,
281           resources_path.Append(
282               extension_misc::kAccessibilityCommonExtensionPath),
283           extension_misc::kAccessibilityCommonManifestFilename,
284           extension_misc::kAccessibilityCommonGuestManifestFilename,
285           base::BindRepeating(
286               &AccessibilityManager::PostUnloadAccessibilityCommon,
287               weak_ptr_factory_.GetWeakPtr())));
288   chromevox_loader_ = base::WrapUnique(new AccessibilityExtensionLoader(
289       extension_misc::kChromeVoxExtensionId,
290       resources_path.Append(extension_misc::kChromeVoxExtensionPath),
291       extension_misc::kChromeVoxManifestFilename,
292       extension_misc::kChromeVoxGuestManifestFilename,
293       base::BindRepeating(&AccessibilityManager::PostUnloadChromeVox,
294                           weak_ptr_factory_.GetWeakPtr())));
295   select_to_speak_loader_ = base::WrapUnique(new AccessibilityExtensionLoader(
296       extension_misc::kSelectToSpeakExtensionId,
297       resources_path.Append(extension_misc::kSelectToSpeakExtensionPath),
298       extension_misc::kSelectToSpeakManifestFilename,
299       extension_misc::kSelectToSpeakGuestManifestFilename,
300       base::BindRepeating(&AccessibilityManager::PostUnloadSelectToSpeak,
301                           weak_ptr_factory_.GetWeakPtr())));
302   switch_access_loader_ = base::WrapUnique(new AccessibilityExtensionLoader(
303       extension_misc::kSwitchAccessExtensionId,
304       resources_path.Append(extension_misc::kSwitchAccessExtensionPath),
305       extension_misc::kSwitchAccessManifestFilename,
306       extension_misc::kSwitchAccessGuestManifestFilename,
307       base::BindRepeating(&AccessibilityManager::PostUnloadSwitchAccess,
308                           weak_ptr_factory_.GetWeakPtr())));
309 
310   // Connect to the media session service.
311   content::GetMediaSessionService().BindAudioFocusManager(
312       audio_focus_manager_.BindNewPipeAndPassReceiver());
313 
314   ash::AcceleratorController::SetVolumeAdjustmentSoundCallback(
315       base::BindRepeating(&AccessibilityManager::PlayVolumeAdjustSound,
316                           base::Unretained(this)));
317 
318   CrasAudioHandler::Get()->AddAudioObserver(this);
319 }
320 
~AccessibilityManager()321 AccessibilityManager::~AccessibilityManager() {
322   CHECK(this == g_accessibility_manager);
323   AccessibilityStatusEventDetails details(ACCESSIBILITY_MANAGER_SHUTDOWN,
324                                           false);
325   NotifyAccessibilityStatusChanged(details);
326   CrasAudioHandler::Get()->RemoveAudioObserver(this);
327   user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
328   input_method::InputMethodManager::Get()->RemoveObserver(this);
329 
330   if (chromevox_panel_) {
331     chromevox_panel_->CloseNow();
332     chromevox_panel_ = nullptr;
333   }
334 
335   ash::AcceleratorController::SetVolumeAdjustmentSoundCallback({});
336 }
337 
ShouldShowAccessibilityMenu()338 bool AccessibilityManager::ShouldShowAccessibilityMenu() {
339   // If any of the loaded profiles has an accessibility feature turned on - or
340   // enforced to always show the menu - we return true to show the menu.
341   // NOTE: This includes the login screen profile, so if a feature is turned on
342   // at the login screen the menu will show even if the user has no features
343   // enabled inside the session. http://crbug.com/755631
344   std::vector<Profile*> profiles =
345       g_browser_process->profile_manager()->GetLoadedProfiles();
346   for (std::vector<Profile*>::iterator it = profiles.begin();
347        it != profiles.end(); ++it) {
348     PrefService* prefs = (*it)->GetPrefs();
349     if (prefs->GetBoolean(ash::prefs::kAccessibilityStickyKeysEnabled) ||
350         prefs->GetBoolean(ash::prefs::kAccessibilityLargeCursorEnabled) ||
351         prefs->GetBoolean(ash::prefs::kAccessibilitySpokenFeedbackEnabled) ||
352         prefs->GetBoolean(ash::prefs::kAccessibilitySelectToSpeakEnabled) ||
353         prefs->GetBoolean(ash::prefs::kAccessibilitySwitchAccessEnabled) ||
354         prefs->GetBoolean(ash::prefs::kAccessibilityHighContrastEnabled) ||
355         prefs->GetBoolean(ash::prefs::kAccessibilityAutoclickEnabled) ||
356         prefs->GetBoolean(ash::prefs::kShouldAlwaysShowAccessibilityMenu) ||
357         prefs->GetBoolean(ash::prefs::kAccessibilityScreenMagnifierEnabled) ||
358         prefs->GetBoolean(ash::prefs::kAccessibilityVirtualKeyboardEnabled) ||
359         prefs->GetBoolean(ash::prefs::kAccessibilityMonoAudioEnabled) ||
360         prefs->GetBoolean(ash::prefs::kAccessibilityCaretHighlightEnabled) ||
361         prefs->GetBoolean(ash::prefs::kAccessibilityCursorHighlightEnabled) ||
362         prefs->GetBoolean(ash::prefs::kAccessibilityFocusHighlightEnabled) ||
363         prefs->GetBoolean(ash::prefs::kAccessibilityDictationEnabled) ||
364         prefs->GetBoolean(ash::prefs::kDockedMagnifierEnabled)) {
365       return true;
366     }
367   }
368   return false;
369 }
370 
UpdateAlwaysShowMenuFromPref()371 void AccessibilityManager::UpdateAlwaysShowMenuFromPref() {
372   if (!profile_)
373     return;
374 
375   // Update system tray menu visibility.
376   ash::AccessibilityController::Get()->NotifyAccessibilityStatusChanged();
377 }
378 
EnableLargeCursor(bool enabled)379 void AccessibilityManager::EnableLargeCursor(bool enabled) {
380   if (!profile_)
381     return;
382 
383   PrefService* pref_service = profile_->GetPrefs();
384   pref_service->SetBoolean(ash::prefs::kAccessibilityLargeCursorEnabled,
385                            enabled);
386   pref_service->CommitPendingWrite();
387 }
388 
OnLargeCursorChanged()389 void AccessibilityManager::OnLargeCursorChanged() {
390   AccessibilityStatusEventDetails details(ACCESSIBILITY_TOGGLE_LARGE_CURSOR,
391                                           IsLargeCursorEnabled());
392   NotifyAccessibilityStatusChanged(details);
393 }
394 
IsLargeCursorEnabled() const395 bool AccessibilityManager::IsLargeCursorEnabled() const {
396   return profile_ && profile_->GetPrefs()->GetBoolean(
397                          ash::prefs::kAccessibilityLargeCursorEnabled);
398 }
399 
EnableStickyKeys(bool enabled)400 void AccessibilityManager::EnableStickyKeys(bool enabled) {
401   if (!profile_)
402     return;
403   PrefService* pref_service = profile_->GetPrefs();
404   pref_service->SetBoolean(ash::prefs::kAccessibilityStickyKeysEnabled,
405                            enabled);
406   pref_service->CommitPendingWrite();
407 }
408 
IsStickyKeysEnabled() const409 bool AccessibilityManager::IsStickyKeysEnabled() const {
410   return profile_ && profile_->GetPrefs()->GetBoolean(
411                          ash::prefs::kAccessibilityStickyKeysEnabled);
412 }
413 
OnStickyKeysChanged()414 void AccessibilityManager::OnStickyKeysChanged() {
415   AccessibilityStatusEventDetails details(ACCESSIBILITY_TOGGLE_STICKY_KEYS,
416                                           IsStickyKeysEnabled());
417   NotifyAccessibilityStatusChanged(details);
418 }
419 
EnableSpokenFeedback(bool enabled)420 void AccessibilityManager::EnableSpokenFeedback(bool enabled) {
421   if (!profile_)
422     return;
423 
424   PrefService* pref_service = profile_->GetPrefs();
425   pref_service->SetBoolean(ash::prefs::kAccessibilitySpokenFeedbackEnabled,
426                            enabled);
427   pref_service->CommitPendingWrite();
428 }
429 
OnSpokenFeedbackChanged()430 void AccessibilityManager::OnSpokenFeedbackChanged() {
431   if (!profile_)
432     return;
433 
434   const bool enabled = profile_->GetPrefs()->GetBoolean(
435       ash::prefs::kAccessibilitySpokenFeedbackEnabled);
436 
437   if (!chromeos::ProfileHelper::IsSigninProfile(profile_) &&
438       !chromeos::ProfileHelper::IsLockScreenAppProfile(profile_)) {
439     user_manager::known_user::SetBooleanPref(
440         multi_user_util::GetAccountIdFromProfile(profile_),
441         kUserSpokenFeedbackEnabled, enabled);
442   }
443 
444   if (enabled) {
445     chromevox_loader_->SetProfile(
446         profile_,
447         base::BindRepeating(&AccessibilityManager::PostSwitchChromeVoxProfile,
448                             weak_ptr_factory_.GetWeakPtr()));
449   }
450 
451   if (spoken_feedback_enabled_ == enabled)
452     return;
453 
454   spoken_feedback_enabled_ = enabled;
455 
456   AccessibilityStatusEventDetails details(ACCESSIBILITY_TOGGLE_SPOKEN_FEEDBACK,
457                                           enabled);
458   NotifyAccessibilityStatusChanged(details);
459 
460   if (enabled) {
461     chromevox_loader_->Load(
462         profile_, base::BindRepeating(&AccessibilityManager::PostLoadChromeVox,
463                                       weak_ptr_factory_.GetWeakPtr()));
464   } else {
465     chromevox_loader_->Unload();
466   }
467   UpdateBrailleImeState();
468 }
469 
IsSpokenFeedbackEnabled() const470 bool AccessibilityManager::IsSpokenFeedbackEnabled() const {
471   return profile_ && profile_->GetPrefs()->GetBoolean(
472                          ash::prefs::kAccessibilitySpokenFeedbackEnabled);
473 }
474 
EnableHighContrast(bool enabled)475 void AccessibilityManager::EnableHighContrast(bool enabled) {
476   if (!profile_)
477     return;
478 
479   PrefService* pref_service = profile_->GetPrefs();
480   pref_service->SetBoolean(ash::prefs::kAccessibilityHighContrastEnabled,
481                            enabled);
482   pref_service->CommitPendingWrite();
483 }
484 
IsHighContrastEnabled() const485 bool AccessibilityManager::IsHighContrastEnabled() const {
486   return profile_ && profile_->GetPrefs()->GetBoolean(
487                          ash::prefs::kAccessibilityHighContrastEnabled);
488 }
489 
OnHighContrastChanged()490 void AccessibilityManager::OnHighContrastChanged() {
491   AccessibilityStatusEventDetails details(
492       ACCESSIBILITY_TOGGLE_HIGH_CONTRAST_MODE, IsHighContrastEnabled());
493   NotifyAccessibilityStatusChanged(details);
494 }
495 
OnLocaleChanged()496 void AccessibilityManager::OnLocaleChanged() {
497   if (!profile_)
498     return;
499 
500   if (!IsSpokenFeedbackEnabled())
501     return;
502 
503   // If the system locale changes and spoken feedback is enabled,
504   // reload ChromeVox so that it switches its internal translations
505   // to the new language.
506   EnableSpokenFeedback(false);
507   EnableSpokenFeedback(true);
508 }
509 
OnViewFocusedInArc(const gfx::Rect & bounds_in_screen,bool is_editable)510 void AccessibilityManager::OnViewFocusedInArc(const gfx::Rect& bounds_in_screen,
511                                               bool is_editable) {
512   ash::AccessibilityController::Get()->SetFocusHighlightRect(bounds_in_screen);
513 
514   MagnificationManager* magnification_manager = MagnificationManager::Get();
515   if (magnification_manager)
516     magnification_manager->HandleFocusedRectChangedIfEnabled(bounds_in_screen,
517                                                              is_editable);
518 }
519 
PlayEarcon(int sound_key,PlaySoundOption option)520 bool AccessibilityManager::PlayEarcon(int sound_key, PlaySoundOption option) {
521   DCHECK(sound_key < chromeos::SOUND_COUNT);
522   base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
523   if (cl->HasSwitch(kAshDisableSystemSounds))
524     return false;
525   if (option == PlaySoundOption::ONLY_IF_SPOKEN_FEEDBACK_ENABLED &&
526       !IsSpokenFeedbackEnabled()) {
527     return false;
528   }
529   return audio::SoundsManager::Get()->Play(sound_key);
530 }
531 
OnTwoFingerTouchStart()532 void AccessibilityManager::OnTwoFingerTouchStart() {
533   if (!profile_)
534     return;
535 
536   extensions::EventRouter* event_router =
537       extensions::EventRouter::Get(profile_);
538 
539   auto event_args = std::make_unique<base::ListValue>();
540   auto event = std::make_unique<extensions::Event>(
541       extensions::events::ACCESSIBILITY_PRIVATE_ON_TWO_FINGER_TOUCH_START,
542       extensions::api::accessibility_private::OnTwoFingerTouchStart::kEventName,
543       std::move(event_args));
544   event_router->BroadcastEvent(std::move(event));
545 }
546 
OnTwoFingerTouchStop()547 void AccessibilityManager::OnTwoFingerTouchStop() {
548   if (!profile_)
549     return;
550 
551   extensions::EventRouter* event_router =
552       extensions::EventRouter::Get(profile_);
553 
554   auto event_args = std::make_unique<base::ListValue>();
555   auto event = std::make_unique<extensions::Event>(
556       extensions::events::ACCESSIBILITY_PRIVATE_ON_TWO_FINGER_TOUCH_STOP,
557       extensions::api::accessibility_private::OnTwoFingerTouchStop::kEventName,
558       std::move(event_args));
559   event_router->BroadcastEvent(std::move(event));
560 }
561 
ShouldToggleSpokenFeedbackViaTouch()562 bool AccessibilityManager::ShouldToggleSpokenFeedbackViaTouch() {
563   return false;
564 }
565 
PlaySpokenFeedbackToggleCountdown(int tick_count)566 bool AccessibilityManager::PlaySpokenFeedbackToggleCountdown(int tick_count) {
567   return audio::SoundsManager::Get()->Play(
568       tick_count % 2 ? SOUND_SPOKEN_FEEDBACK_TOGGLE_COUNTDOWN_HIGH
569                      : SOUND_SPOKEN_FEEDBACK_TOGGLE_COUNTDOWN_LOW);
570 }
571 
HandleAccessibilityGesture(ax::mojom::Gesture gesture,gfx::PointF location)572 void AccessibilityManager::HandleAccessibilityGesture(
573     ax::mojom::Gesture gesture,
574     gfx::PointF location) {
575   extensions::EventRouter* event_router =
576       extensions::EventRouter::Get(profile_);
577 
578   std::unique_ptr<base::ListValue> event_args =
579       std::make_unique<base::ListValue>();
580   event_args->AppendString(ui::ToString(gesture));
581   event_args->AppendInteger(location.x());
582   event_args->AppendInteger(location.y());
583   std::unique_ptr<extensions::Event> event(new extensions::Event(
584       extensions::events::ACCESSIBILITY_PRIVATE_ON_ACCESSIBILITY_GESTURE,
585       extensions::api::accessibility_private::OnAccessibilityGesture::
586           kEventName,
587       std::move(event_args)));
588   event_router->DispatchEventWithLazyListener(
589       extension_misc::kChromeVoxExtensionId, std::move(event));
590 }
591 
SetTouchAccessibilityAnchorPoint(const gfx::Point & anchor_point)592 void AccessibilityManager::SetTouchAccessibilityAnchorPoint(
593     const gfx::Point& anchor_point) {
594   for (auto* rwc : ash::RootWindowController::root_window_controllers())
595     rwc->SetTouchAccessibilityAnchorPoint(anchor_point);
596 }
597 
EnableAutoclick(bool enabled)598 void AccessibilityManager::EnableAutoclick(bool enabled) {
599   if (!profile_)
600     return;
601 
602   PrefService* pref_service = profile_->GetPrefs();
603   pref_service->SetBoolean(ash::prefs::kAccessibilityAutoclickEnabled, enabled);
604   pref_service->CommitPendingWrite();
605 }
606 
IsAutoclickEnabled() const607 bool AccessibilityManager::IsAutoclickEnabled() const {
608   return profile_ && profile_->GetPrefs()->GetBoolean(
609                          ash::prefs::kAccessibilityAutoclickEnabled);
610 }
611 
OnAccessibilityCommonChanged(const std::string & pref_name)612 void AccessibilityManager::OnAccessibilityCommonChanged(
613     const std::string& pref_name) {
614   if (!profile_)
615     return;
616 
617   const bool enabled = profile_->GetPrefs()->GetBoolean(pref_name);
618   if (enabled) {
619     accessibility_common_extension_loader_->SetProfile(
620         profile_, base::Closure() /* done_callback */);
621   }
622 
623   size_t pref_count = accessibility_common_enabled_features_.count(pref_name);
624   if ((pref_count != 0 && enabled) || (pref_count == 0 && !enabled))
625     return;
626 
627   if (enabled) {
628     accessibility_common_enabled_features_.insert(pref_name);
629     if (!accessibility_common_extension_loader_->loaded()) {
630       accessibility_common_extension_loader_->Load(
631           profile_, base::BindRepeating(
632                         &AccessibilityManager::PostLoadAccessibilityCommon,
633                         weak_ptr_factory_.GetWeakPtr()));
634     } else {
635       // It's already loaded. Just run the callback.
636       PostLoadAccessibilityCommon();
637     }
638   } else {
639     accessibility_common_enabled_features_.erase(pref_name);
640 
641     if (accessibility_common_enabled_features_.empty()) {
642       accessibility_common_extension_loader_->Unload();
643     }
644   }
645 }
646 
RequestAutoclickScrollableBoundsForPoint(gfx::Point & point_in_screen)647 void AccessibilityManager::RequestAutoclickScrollableBoundsForPoint(
648     gfx::Point& point_in_screen) {
649   extensions::EventRouter* event_router =
650       extensions::EventRouter::Get(profile_);
651   std::unique_ptr<base::ListValue> event_args = extensions::api::
652       accessibility_private::OnScrollableBoundsForPointRequested::Create(
653           point_in_screen.x(), point_in_screen.y());
654   std::unique_ptr<extensions::Event> event =
655       std::make_unique<extensions::Event>(
656           extensions::events::
657               ACCESSIBILITY_PRIVATE_FIND_SCROLLABLE_BOUNDS_FOR_POINT,
658           extensions::api::accessibility_private::
659               OnScrollableBoundsForPointRequested::kEventName,
660           std::move(event_args));
661   event_router->DispatchEventWithLazyListener(
662       extension_misc::kAccessibilityCommonExtensionId, std::move(event));
663 }
664 
MagnifierBoundsChanged(const gfx::Rect & bounds_in_screen)665 void AccessibilityManager::MagnifierBoundsChanged(
666     const gfx::Rect& bounds_in_screen) {
667   if (!profile_)
668     return;
669 
670   extensions::EventRouter* event_router =
671       extensions::EventRouter::Get(profile_);
672 
673   auto magnifier_bounds =
674       std::make_unique<extensions::api::accessibility_private::ScreenRect>();
675   magnifier_bounds->left = bounds_in_screen.x();
676   magnifier_bounds->top = bounds_in_screen.y();
677   magnifier_bounds->width = bounds_in_screen.width();
678   magnifier_bounds->height = bounds_in_screen.height();
679 
680   auto event_args =
681       extensions::api::accessibility_private::OnMagnifierBoundsChanged::Create(
682           *magnifier_bounds.get());
683 
684   auto event = std::make_unique<extensions::Event>(
685       extensions::events::ACCESSIBILITY_PRIVATE_ON_MAGNIFIER_BOUNDS_CHANGED,
686       extensions::api::accessibility_private::OnMagnifierBoundsChanged::
687           kEventName,
688       std::move(event_args));
689 
690   event_router->DispatchEventWithLazyListener(
691       extension_misc::kAccessibilityCommonExtensionId, std::move(event));
692 }
693 
EnableVirtualKeyboard(bool enabled)694 void AccessibilityManager::EnableVirtualKeyboard(bool enabled) {
695   if (!profile_)
696     return;
697 
698   PrefService* pref_service = profile_->GetPrefs();
699   pref_service->SetBoolean(ash::prefs::kAccessibilityVirtualKeyboardEnabled,
700                            enabled);
701   pref_service->CommitPendingWrite();
702 }
703 
IsVirtualKeyboardEnabled() const704 bool AccessibilityManager::IsVirtualKeyboardEnabled() const {
705   return profile_ && profile_->GetPrefs()->GetBoolean(
706                          ash::prefs::kAccessibilityVirtualKeyboardEnabled);
707 }
708 
OnVirtualKeyboardChanged()709 void AccessibilityManager::OnVirtualKeyboardChanged() {
710   AccessibilityStatusEventDetails details(ACCESSIBILITY_TOGGLE_VIRTUAL_KEYBOARD,
711                                           IsVirtualKeyboardEnabled());
712   NotifyAccessibilityStatusChanged(details);
713 }
714 
EnableMonoAudio(bool enabled)715 void AccessibilityManager::EnableMonoAudio(bool enabled) {
716   if (!profile_)
717     return;
718 
719   PrefService* pref_service = profile_->GetPrefs();
720   pref_service->SetBoolean(ash::prefs::kAccessibilityMonoAudioEnabled, enabled);
721   pref_service->CommitPendingWrite();
722 }
723 
IsMonoAudioEnabled() const724 bool AccessibilityManager::IsMonoAudioEnabled() const {
725   return profile_ && profile_->GetPrefs()->GetBoolean(
726                          ash::prefs::kAccessibilityMonoAudioEnabled);
727 }
728 
OnMonoAudioChanged()729 void AccessibilityManager::OnMonoAudioChanged() {
730   AccessibilityStatusEventDetails details(ACCESSIBILITY_TOGGLE_MONO_AUDIO,
731                                           IsMonoAudioEnabled());
732   NotifyAccessibilityStatusChanged(details);
733 }
734 
SetDarkenScreen(bool darken)735 void AccessibilityManager::SetDarkenScreen(bool darken) {
736   ash::AccessibilityController::Get()->SetDarkenScreen(darken);
737 }
738 
SetCaretHighlightEnabled(bool enabled)739 void AccessibilityManager::SetCaretHighlightEnabled(bool enabled) {
740   if (!profile_)
741     return;
742 
743   PrefService* pref_service = profile_->GetPrefs();
744   pref_service->SetBoolean(ash::prefs::kAccessibilityCaretHighlightEnabled,
745                            enabled);
746   pref_service->CommitPendingWrite();
747 }
748 
IsCaretHighlightEnabled() const749 bool AccessibilityManager::IsCaretHighlightEnabled() const {
750   return profile_ && profile_->GetPrefs()->GetBoolean(
751                          ash::prefs::kAccessibilityCaretHighlightEnabled);
752 }
753 
OnCaretHighlightChanged()754 void AccessibilityManager::OnCaretHighlightChanged() {
755   AccessibilityStatusEventDetails details(ACCESSIBILITY_TOGGLE_CARET_HIGHLIGHT,
756                                           IsCaretHighlightEnabled());
757   NotifyAccessibilityStatusChanged(details);
758 }
759 
SetCursorHighlightEnabled(bool enabled)760 void AccessibilityManager::SetCursorHighlightEnabled(bool enabled) {
761   if (!profile_)
762     return;
763 
764   PrefService* pref_service = profile_->GetPrefs();
765   pref_service->SetBoolean(ash::prefs::kAccessibilityCursorHighlightEnabled,
766                            enabled);
767   pref_service->CommitPendingWrite();
768 }
769 
IsCursorHighlightEnabled() const770 bool AccessibilityManager::IsCursorHighlightEnabled() const {
771   return profile_ && profile_->GetPrefs()->GetBoolean(
772                          ash::prefs::kAccessibilityCursorHighlightEnabled);
773 }
774 
OnCursorHighlightChanged()775 void AccessibilityManager::OnCursorHighlightChanged() {
776   AccessibilityStatusEventDetails details(ACCESSIBILITY_TOGGLE_CURSOR_HIGHLIGHT,
777                                           IsCursorHighlightEnabled());
778   NotifyAccessibilityStatusChanged(details);
779 }
780 
IsDictationEnabled() const781 bool AccessibilityManager::IsDictationEnabled() const {
782   return profile_ && profile_->GetPrefs()->GetBoolean(
783                          ash::prefs::kAccessibilityDictationEnabled);
784 }
785 
SetFocusHighlightEnabled(bool enabled)786 void AccessibilityManager::SetFocusHighlightEnabled(bool enabled) {
787   if (!profile_)
788     return;
789 
790   PrefService* pref_service = profile_->GetPrefs();
791   pref_service->SetBoolean(ash::prefs::kAccessibilityFocusHighlightEnabled,
792                            enabled);
793   pref_service->CommitPendingWrite();
794 }
795 
IsFocusHighlightEnabled() const796 bool AccessibilityManager::IsFocusHighlightEnabled() const {
797   return profile_ && profile_->GetPrefs()->GetBoolean(
798                          ash::prefs::kAccessibilityFocusHighlightEnabled);
799 }
800 
OnFocusHighlightChanged()801 void AccessibilityManager::OnFocusHighlightChanged() {
802   bool enabled = IsFocusHighlightEnabled();
803 
804   // Focus highlighting can't be on when spoken feedback is on, because
805   // ChromeVox does its own focus highlighting.
806   if (IsSpokenFeedbackEnabled())
807     enabled = false;
808   AccessibilityStatusEventDetails details(ACCESSIBILITY_TOGGLE_FOCUS_HIGHLIGHT,
809                                           enabled);
810   NotifyAccessibilityStatusChanged(details);
811 
812   // TODO(crbug.com/1096759): Load or unload the AccessibilityCommon extension
813   // which will be used to get the currently focused view. This will enable us
814   // to get views where focus is only represented in the accessibility tree,
815   // like those set by aria-activedescendant. It will also possibly eliminate
816   // the need for OnViewFocusedInArc.
817 }
818 
SetSelectToSpeakEnabled(bool enabled)819 void AccessibilityManager::SetSelectToSpeakEnabled(bool enabled) {
820   if (!profile_)
821     return;
822 
823   PrefService* pref_service = profile_->GetPrefs();
824   pref_service->SetBoolean(ash::prefs::kAccessibilitySelectToSpeakEnabled,
825                            enabled);
826   pref_service->CommitPendingWrite();
827 }
828 
IsSelectToSpeakEnabled() const829 bool AccessibilityManager::IsSelectToSpeakEnabled() const {
830   return select_to_speak_enabled_;
831 }
832 
RequestSelectToSpeakStateChange()833 void AccessibilityManager::RequestSelectToSpeakStateChange() {
834   extensions::EventRouter* event_router =
835       extensions::EventRouter::Get(profile_);
836 
837   // Send an event to the Select-to-Speak extension requesting a state change.
838   std::unique_ptr<base::ListValue> event_args =
839       std::make_unique<base::ListValue>();
840   std::unique_ptr<extensions::Event> event(new extensions::Event(
841       extensions::events::
842           ACCESSIBILITY_PRIVATE_ON_SELECT_TO_SPEAK_STATE_CHANGE_REQUESTED,
843       extensions::api::accessibility_private::
844           OnSelectToSpeakStateChangeRequested::kEventName,
845       std::move(event_args)));
846   event_router->DispatchEventWithLazyListener(
847       extension_misc::kSelectToSpeakExtensionId, std::move(event));
848 }
849 
SetSelectToSpeakState(ash::SelectToSpeakState state)850 void AccessibilityManager::SetSelectToSpeakState(
851     ash::SelectToSpeakState state) {
852   ash::AccessibilityController::Get()->SetSelectToSpeakState(state);
853 
854   if (select_to_speak_state_observer_for_test_)
855     select_to_speak_state_observer_for_test_.Run();
856 }
857 
OnSelectToSpeakChanged()858 void AccessibilityManager::OnSelectToSpeakChanged() {
859   if (!profile_)
860     return;
861 
862   const bool enabled = profile_->GetPrefs()->GetBoolean(
863       ash::prefs::kAccessibilitySelectToSpeakEnabled);
864   if (enabled)
865     select_to_speak_loader_->SetProfile(profile_, base::Closure());
866 
867   if (select_to_speak_enabled_ == enabled)
868     return;
869 
870   select_to_speak_enabled_ = enabled;
871 
872   AccessibilityStatusEventDetails details(ACCESSIBILITY_TOGGLE_SELECT_TO_SPEAK,
873                                           enabled);
874   NotifyAccessibilityStatusChanged(details);
875 
876   if (enabled) {
877     select_to_speak_loader_->Load(
878         profile_,
879         base::BindRepeating(&AccessibilityManager::PostLoadSelectToSpeak,
880                             weak_ptr_factory_.GetWeakPtr()));
881     // Construct a delegate to connect SelectToSpeak and its EventHandler in
882     // ash.
883     select_to_speak_event_handler_delegate_ =
884         std::make_unique<chromeos::SelectToSpeakEventHandlerDelegate>();
885   } else {
886     select_to_speak_loader_->Unload();
887     select_to_speak_event_handler_delegate_.reset();
888   }
889 }
890 
SetSwitchAccessEnabled(bool enabled)891 void AccessibilityManager::SetSwitchAccessEnabled(bool enabled) {
892   if (!profile_)
893     return;
894 
895   PrefService* pref_service = profile_->GetPrefs();
896   pref_service->SetBoolean(ash::prefs::kAccessibilitySwitchAccessEnabled,
897                            enabled);
898   pref_service->CommitPendingWrite();
899 }
900 
IsSwitchAccessEnabled() const901 bool AccessibilityManager::IsSwitchAccessEnabled() const {
902   return switch_access_enabled_;
903 }
904 
OnSwitchAccessChanged()905 void AccessibilityManager::OnSwitchAccessChanged() {
906   if (!profile_)
907     return;
908 
909   const bool enabled = profile_->GetPrefs()->GetBoolean(
910       ash::prefs::kAccessibilitySwitchAccessEnabled);
911 
912   if (enabled) {
913     // Only update |was_vk_enabled_before_switch_access_| if the profile
914     // changed.
915     if (profile_ != switch_access_loader_->profile()) {
916       was_vk_enabled_before_switch_access_ =
917           ChromeKeyboardControllerClient::Get()->IsEnableFlagSet(
918               keyboard::KeyboardEnableFlag::kExtensionEnabled);
919     }
920 
921     switch_access_loader_->SetProfile(profile_, base::Closure());
922 
923     // Make sure we always update the VK state, on every profile transition.
924     ChromeKeyboardControllerClient::Get()->SetEnableFlag(
925         keyboard::KeyboardEnableFlag::kExtensionEnabled);
926   }
927 
928   if (switch_access_enabled_ == enabled)
929     return;
930   switch_access_enabled_ = enabled;
931 
932   AccessibilityStatusEventDetails details(ACCESSIBILITY_TOGGLE_SWITCH_ACCESS,
933                                           enabled);
934   NotifyAccessibilityStatusChanged(details);
935 
936   if (enabled) {
937     switch_access_loader_->Load(
938         profile_,
939         base::BindRepeating(&AccessibilityManager::PostLoadSwitchAccess,
940                             weak_ptr_factory_.GetWeakPtr()));
941   }
942 }
943 
OnSwitchAccessDisabled()944 void AccessibilityManager::OnSwitchAccessDisabled() {
945   switch_access_loader_->Unload();
946 }
947 
IsBrailleDisplayConnected() const948 bool AccessibilityManager::IsBrailleDisplayConnected() const {
949   return braille_display_connected_;
950 }
951 
CheckBrailleState()952 void AccessibilityManager::CheckBrailleState() {
953   BrailleController* braille_controller = GetBrailleController();
954   if (!scoped_braille_observer_.IsObserving(braille_controller))
955     scoped_braille_observer_.Add(braille_controller);
956   content::GetIOThreadTaskRunner({})->PostTaskAndReplyWithResult(
957       FROM_HERE,
958       base::BindOnce(&BrailleController::GetDisplayState,
959                      base::Unretained(braille_controller)),
960       base::BindOnce(&AccessibilityManager::ReceiveBrailleDisplayState,
961                      weak_ptr_factory_.GetWeakPtr()));
962 }
963 
ReceiveBrailleDisplayState(std::unique_ptr<extensions::api::braille_display_private::DisplayState> state)964 void AccessibilityManager::ReceiveBrailleDisplayState(
965     std::unique_ptr<extensions::api::braille_display_private::DisplayState>
966         state) {
967   OnBrailleDisplayStateChanged(*state);
968 }
969 
UpdateBrailleImeState()970 void AccessibilityManager::UpdateBrailleImeState() {
971   if (!profile_)
972     return;
973   PrefService* pref_service = profile_->GetPrefs();
974   std::string preload_engines_str =
975       pref_service->GetString(prefs::kLanguagePreloadEngines);
976   std::vector<base::StringPiece> preload_engines = base::SplitStringPiece(
977       preload_engines_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
978   std::vector<base::StringPiece>::iterator it =
979       std::find(preload_engines.begin(), preload_engines.end(),
980                 extension_ime_util::kBrailleImeEngineId);
981   bool is_enabled = (it != preload_engines.end());
982   bool should_be_enabled =
983       (IsSpokenFeedbackEnabled() && braille_display_connected_);
984   if (is_enabled == should_be_enabled)
985     return;
986   if (should_be_enabled)
987     preload_engines.push_back(extension_ime_util::kBrailleImeEngineId);
988   else
989     preload_engines.erase(it);
990   pref_service->SetString(prefs::kLanguagePreloadEngines,
991                           base::JoinString(preload_engines, ","));
992   braille_ime_current_ = false;
993 }
994 
995 // Overridden from InputMethodManager::Observer.
InputMethodChanged(input_method::InputMethodManager * manager,Profile *,bool show_message)996 void AccessibilityManager::InputMethodChanged(
997     input_method::InputMethodManager* manager,
998     Profile* /* profile */,
999     bool show_message) {
1000     ash::Shell::Get()->sticky_keys_controller()->SetModifiersEnabled(
1001         manager->IsISOLevel5ShiftUsedByCurrentInputMethod(),
1002         manager->IsAltGrUsedByCurrentInputMethod());
1003   const chromeos::input_method::InputMethodDescriptor descriptor =
1004       manager->GetActiveIMEState()->GetCurrentInputMethod();
1005   braille_ime_current_ =
1006       (descriptor.id() == extension_ime_util::kBrailleImeEngineId);
1007 }
1008 
OnActiveOutputNodeChanged()1009 void AccessibilityManager::OnActiveOutputNodeChanged() {
1010   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
1011           chromeos::switches::kFirstExecAfterBoot))
1012     return;
1013 
1014   AudioDevice device;
1015   CrasAudioHandler::Get()->GetPrimaryActiveOutputDevice(&device);
1016   if (device.type == AudioDeviceType::AUDIO_TYPE_OTHER)
1017     return;
1018 
1019   CrasAudioHandler::Get()->RemoveAudioObserver(this);
1020   if (GetStartupSoundEnabled()) {
1021     PlayEarcon(SOUND_STARTUP, PlaySoundOption::ALWAYS);
1022     return;
1023   }
1024 
1025   const auto& account_ids = user_manager::known_user::GetKnownAccountIds();
1026   for (size_t i = 0; i < account_ids.size(); ++i) {
1027     bool val;
1028     if (user_manager::known_user::GetBooleanPref(
1029             account_ids[i], kUserSpokenFeedbackEnabled, &val) &&
1030         val) {
1031       PlayEarcon(SOUND_STARTUP, PlaySoundOption::ALWAYS);
1032       break;
1033     }
1034   }
1035 }
1036 
OnProfileWillBeDestroyed(Profile * profile)1037 void AccessibilityManager::OnProfileWillBeDestroyed(Profile* profile) {
1038   DCHECK_EQ(profile_, profile);
1039   SetProfile(nullptr);
1040 }
1041 
SetProfile(Profile * profile)1042 void AccessibilityManager::SetProfile(Profile* profile) {
1043   if (profile_ == profile)
1044     return;
1045 
1046   if (profile_)
1047     profile_observer_.Remove(profile_);
1048   DCHECK(!profile_observer_.IsObservingSources());
1049 
1050   pref_change_registrar_.reset();
1051   local_state_pref_change_registrar_.reset();
1052 
1053   // Clear all dictation state on profile change.
1054   dictation_.reset();
1055 
1056   // All features supported by accessibility common.
1057   static const char* kAccessibilityCommonFeatures[] = {
1058       ash::prefs::kAccessibilityAutoclickEnabled,
1059       ash::prefs::kAccessibilityScreenMagnifierEnabled,
1060       ash::prefs::kDockedMagnifierEnabled};
1061 
1062   if (profile) {
1063     // TODO(yoshiki): Move following code to PrefHandler.
1064     pref_change_registrar_.reset(new PrefChangeRegistrar);
1065     pref_change_registrar_->Init(profile->GetPrefs());
1066     pref_change_registrar_->Add(
1067         ash::prefs::kShouldAlwaysShowAccessibilityMenu,
1068         base::BindRepeating(&AccessibilityManager::UpdateAlwaysShowMenuFromPref,
1069                             base::Unretained(this)));
1070     pref_change_registrar_->Add(
1071         ash::prefs::kAccessibilityLargeCursorEnabled,
1072         base::BindRepeating(&AccessibilityManager::OnLargeCursorChanged,
1073                             base::Unretained(this)));
1074     pref_change_registrar_->Add(
1075         ash::prefs::kAccessibilityLargeCursorDipSize,
1076         base::BindRepeating(&AccessibilityManager::OnLargeCursorChanged,
1077                             base::Unretained(this)));
1078     pref_change_registrar_->Add(
1079         ash::prefs::kAccessibilityStickyKeysEnabled,
1080         base::BindRepeating(&AccessibilityManager::OnStickyKeysChanged,
1081                             base::Unretained(this)));
1082     pref_change_registrar_->Add(
1083         ash::prefs::kAccessibilitySpokenFeedbackEnabled,
1084         base::BindRepeating(&AccessibilityManager::OnSpokenFeedbackChanged,
1085                             base::Unretained(this)));
1086     pref_change_registrar_->Add(
1087         ash::prefs::kAccessibilityHighContrastEnabled,
1088         base::BindRepeating(&AccessibilityManager::OnHighContrastChanged,
1089                             base::Unretained(this)));
1090     pref_change_registrar_->Add(
1091         ash::prefs::kAccessibilityVirtualKeyboardEnabled,
1092         base::BindRepeating(&AccessibilityManager::OnVirtualKeyboardChanged,
1093                             base::Unretained(this)));
1094     pref_change_registrar_->Add(
1095         ash::prefs::kAccessibilityMonoAudioEnabled,
1096         base::BindRepeating(&AccessibilityManager::OnMonoAudioChanged,
1097                             base::Unretained(this)));
1098     pref_change_registrar_->Add(
1099         ash::prefs::kAccessibilityCaretHighlightEnabled,
1100         base::BindRepeating(&AccessibilityManager::OnCaretHighlightChanged,
1101                             base::Unretained(this)));
1102     pref_change_registrar_->Add(
1103         ash::prefs::kAccessibilityCursorHighlightEnabled,
1104         base::BindRepeating(&AccessibilityManager::OnCursorHighlightChanged,
1105                             base::Unretained(this)));
1106     pref_change_registrar_->Add(
1107         ash::prefs::kAccessibilityFocusHighlightEnabled,
1108         base::BindRepeating(&AccessibilityManager::OnFocusHighlightChanged,
1109                             base::Unretained(this)));
1110     pref_change_registrar_->Add(
1111         ash::prefs::kAccessibilitySelectToSpeakEnabled,
1112         base::BindRepeating(&AccessibilityManager::OnSelectToSpeakChanged,
1113                             base::Unretained(this)));
1114     pref_change_registrar_->Add(
1115         ash::prefs::kAccessibilitySwitchAccessEnabled,
1116         base::BindRepeating(&AccessibilityManager::OnSwitchAccessChanged,
1117                             base::Unretained(this)));
1118 
1119     for (const std::string& feature : kAccessibilityCommonFeatures) {
1120       pref_change_registrar_->Add(
1121           feature, base::BindRepeating(
1122                        &AccessibilityManager::OnAccessibilityCommonChanged,
1123                        base::Unretained(this)));
1124     }
1125 
1126     local_state_pref_change_registrar_.reset(new PrefChangeRegistrar);
1127     local_state_pref_change_registrar_->Init(g_browser_process->local_state());
1128     local_state_pref_change_registrar_->Add(
1129         language::prefs::kApplicationLocale,
1130         base::BindRepeating(&AccessibilityManager::OnLocaleChanged,
1131                             base::Unretained(this)));
1132 
1133     // Compute these histograms on the main (UI) thread because they
1134     // need to access PrefService.
1135     content::BrowserAccessibilityState::GetInstance()
1136         ->AddUIThreadHistogramCallback(base::BindOnce(
1137             &AccessibilityManager::UpdateChromeOSAccessibilityHistograms,
1138             base::Unretained(this)));
1139 
1140     extensions::ExtensionRegistry* registry =
1141         extensions::ExtensionRegistry::Get(profile);
1142     if (!extension_registry_observer_.IsObserving(registry))
1143       extension_registry_observer_.Add(registry);
1144 
1145     profile_observer_.Add(profile);
1146   }
1147 
1148   bool had_profile = (profile_ != NULL);
1149   profile_ = profile;
1150 
1151   if (!had_profile && profile)
1152     CheckBrailleState();
1153   else
1154     UpdateBrailleImeState();
1155   UpdateAlwaysShowMenuFromPref();
1156 
1157   // TODO(warx): reconcile to ash once the prefs registration above is moved to
1158   // ash.
1159   OnSpokenFeedbackChanged();
1160   OnSwitchAccessChanged();
1161   OnSelectToSpeakChanged();
1162 
1163   for (const std::string& feature : kAccessibilityCommonFeatures)
1164     OnAccessibilityCommonChanged(feature);
1165 }
1166 
SetProfileByUser(const user_manager::User * user)1167 void AccessibilityManager::SetProfileByUser(const user_manager::User* user) {
1168   Profile* profile = ProfileHelper::Get()->GetProfileByUser(user);
1169   DCHECK(profile);
1170   SetProfile(profile);
1171 }
1172 
ActiveUserChanged(user_manager::User * active_user)1173 void AccessibilityManager::ActiveUserChanged(user_manager::User* active_user) {
1174   if (!active_user)
1175     return;
1176 
1177   active_user->AddProfileCreatedObserver(
1178       base::BindOnce(&AccessibilityManager::SetProfileByUser,
1179                      weak_ptr_factory_.GetWeakPtr(), active_user));
1180 }
1181 
PlayShutdownSound()1182 base::TimeDelta AccessibilityManager::PlayShutdownSound() {
1183   if (!PlayEarcon(SOUND_SHUTDOWN,
1184                   PlaySoundOption::ONLY_IF_SPOKEN_FEEDBACK_ENABLED)) {
1185     return base::TimeDelta();
1186   }
1187   return audio::SoundsManager::Get()->GetDuration(SOUND_SHUTDOWN);
1188 }
1189 
1190 std::unique_ptr<AccessibilityStatusSubscription>
RegisterCallback(const AccessibilityStatusCallback & cb)1191 AccessibilityManager::RegisterCallback(const AccessibilityStatusCallback& cb) {
1192   return callback_list_.Add(cb);
1193 }
1194 
NotifyAccessibilityStatusChanged(const AccessibilityStatusEventDetails & details)1195 void AccessibilityManager::NotifyAccessibilityStatusChanged(
1196     const AccessibilityStatusEventDetails& details) {
1197   callback_list_.Notify(details);
1198 
1199   if (details.notification_type == ACCESSIBILITY_TOGGLE_DICTATION) {
1200     ash::AccessibilityController::Get()->SetDictationActive(details.enabled);
1201     ash::AccessibilityController::Get()->NotifyAccessibilityStatusChanged();
1202     return;
1203   }
1204 
1205   // Update system tray menu visibility. Prefs tracked inside ash handle their
1206   // own updates to avoid race conditions (pref updates are asynchronous between
1207   // chrome and ash).
1208   if (details.notification_type == ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFIER ||
1209       details.notification_type == ACCESSIBILITY_TOGGLE_DICTATION) {
1210     ash::AccessibilityController::Get()->NotifyAccessibilityStatusChanged();
1211   }
1212 }
1213 
UpdateChromeOSAccessibilityHistograms()1214 void AccessibilityManager::UpdateChromeOSAccessibilityHistograms() {
1215   base::UmaHistogramBoolean("Accessibility.CrosSpokenFeedback",
1216                             IsSpokenFeedbackEnabled());
1217   base::UmaHistogramBoolean("Accessibility.CrosHighContrast",
1218                             IsHighContrastEnabled());
1219   base::UmaHistogramBoolean("Accessibility.CrosVirtualKeyboard",
1220                             IsVirtualKeyboardEnabled());
1221   base::UmaHistogramBoolean("Accessibility.CrosStickyKeys",
1222                             IsStickyKeysEnabled());
1223   if (MagnificationManager::Get()) {
1224     base::UmaHistogramBoolean(
1225         "Accessibility.CrosScreenMagnifier",
1226         MagnificationManager::Get()->IsMagnifierEnabled());
1227     base::UmaHistogramBoolean(
1228         "Accessibility.CrosDockedMagnifier",
1229         MagnificationManager::Get()->IsDockedMagnifierEnabled());
1230   }
1231   if (profile_) {
1232     const PrefService* const prefs = profile_->GetPrefs();
1233 
1234     bool large_cursor_enabled =
1235         prefs->GetBoolean(ash::prefs::kAccessibilityLargeCursorEnabled);
1236     base::UmaHistogramBoolean("Accessibility.CrosLargeCursor",
1237                               large_cursor_enabled);
1238     if (large_cursor_enabled) {
1239       base::UmaHistogramCounts100(
1240           "Accessibility.CrosLargeCursorSize",
1241           prefs->GetInteger(ash::prefs::kAccessibilityLargeCursorDipSize));
1242     }
1243 
1244     base::UmaHistogramBoolean(
1245         "Accessibility.CrosAlwaysShowA11yMenu",
1246         prefs->GetBoolean(ash::prefs::kShouldAlwaysShowAccessibilityMenu));
1247 
1248     bool autoclick_enabled =
1249         prefs->GetBoolean(ash::prefs::kAccessibilityAutoclickEnabled);
1250     base::UmaHistogramBoolean("Accessibility.CrosAutoclick", autoclick_enabled);
1251 
1252     base::UmaHistogramBoolean(
1253         "Accessibility.CrosCursorColor",
1254         prefs->GetBoolean(ash::prefs::kAccessibilityCursorColorEnabled));
1255   }
1256   base::UmaHistogramBoolean("Accessibility.CrosCaretHighlight",
1257                             IsCaretHighlightEnabled());
1258   base::UmaHistogramBoolean("Accessibility.CrosCursorHighlight",
1259                             IsCursorHighlightEnabled());
1260   base::UmaHistogramBoolean("Accessibility.CrosDictation",
1261                             IsDictationEnabled());
1262   base::UmaHistogramBoolean("Accessibility.CrosFocusHighlight",
1263                             IsFocusHighlightEnabled());
1264   base::UmaHistogramBoolean("Accessibility.CrosSelectToSpeak",
1265                             IsSelectToSpeakEnabled());
1266   base::UmaHistogramBoolean("Accessibility.CrosSwitchAccess",
1267                             IsSwitchAccessEnabled());
1268 }
1269 
PlayVolumeAdjustSound()1270 void AccessibilityManager::PlayVolumeAdjustSound() {
1271   if (VolumeAdjustSoundEnabled()) {
1272     PlayEarcon(chromeos::SOUND_VOLUME_ADJUST,
1273                chromeos::PlaySoundOption::ONLY_IF_SPOKEN_FEEDBACK_ENABLED);
1274   }
1275 }
1276 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)1277 void AccessibilityManager::Observe(
1278     int type,
1279     const content::NotificationSource& source,
1280     const content::NotificationDetails& details) {
1281   switch (type) {
1282     case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
1283       // Update |profile_| when entering the login screen.
1284       Profile* profile = ProfileManager::GetActiveUserProfile();
1285       if (ProfileHelper::IsSigninProfile(profile))
1286         SetProfile(profile);
1287       break;
1288     }
1289     case chrome::NOTIFICATION_APP_TERMINATING: {
1290       app_terminating_ = true;
1291       break;
1292     }
1293     case content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE: {
1294       // Avoid unnecessary mojo IPC to ash when focus highlight feature is not
1295       // enabled.
1296       if (!IsFocusHighlightEnabled())
1297         return;
1298       content::FocusedNodeDetails* node_details =
1299           content::Details<content::FocusedNodeDetails>(details).ptr();
1300       ash::AccessibilityController::Get()->SetFocusHighlightRect(
1301           node_details->node_bounds_in_screen);
1302       break;
1303     }
1304   }
1305 }
1306 
OnBrailleDisplayStateChanged(const DisplayState & display_state)1307 void AccessibilityManager::OnBrailleDisplayStateChanged(
1308     const DisplayState& display_state) {
1309   braille_display_connected_ = display_state.available;
1310   ash::AccessibilityController::Get()->BrailleDisplayStateChanged(
1311       braille_display_connected_);
1312   UpdateBrailleImeState();
1313 }
1314 
OnBrailleKeyEvent(const KeyEvent & event)1315 void AccessibilityManager::OnBrailleKeyEvent(const KeyEvent& event) {
1316   // Ensure the braille IME is active on braille keyboard (dots) input.
1317   if ((event.command ==
1318        extensions::api::braille_display_private::KEY_COMMAND_DOTS) &&
1319       !braille_ime_current_) {
1320     input_method::InputMethodManager::Get()
1321         ->GetActiveIMEState()
1322         ->ChangeInputMethod(extension_ime_util::kBrailleImeEngineId,
1323                             false /* show_message */);
1324   }
1325 }
1326 
OnExtensionUnloaded(content::BrowserContext * browser_context,const extensions::Extension * extension,extensions::UnloadedExtensionReason reason)1327 void AccessibilityManager::OnExtensionUnloaded(
1328     content::BrowserContext* browser_context,
1329     const extensions::Extension* extension,
1330     extensions::UnloadedExtensionReason reason) {
1331   if (extension->id() == keyboard_listener_extension_id_)
1332     keyboard_listener_extension_id_ = std::string();
1333 
1334   if (extension->id() == extension_misc::kSwitchAccessExtensionId) {
1335     extensions::VirtualKeyboardAPI* api =
1336         extensions::BrowserContextKeyedAPIFactory<
1337             extensions::VirtualKeyboardAPI>::Get(browser_context);
1338     DCHECK(api);
1339     api->delegate()->SetRequestedKeyboardState(
1340         extensions::api::virtual_keyboard_private::KEYBOARD_STATE_AUTO);
1341   }
1342 }
1343 
OnShutdown(extensions::ExtensionRegistry * registry)1344 void AccessibilityManager::OnShutdown(extensions::ExtensionRegistry* registry) {
1345   extension_registry_observer_.Remove(registry);
1346 }
1347 
PostLoadChromeVox()1348 void AccessibilityManager::PostLoadChromeVox() {
1349   // In browser_tests loading the ChromeVox extension can race with shutdown.
1350   // http://crbug.com/801700
1351   if (app_terminating_)
1352     return;
1353 
1354   // Do any setup work needed immediately after ChromeVox actually loads.
1355   const std::string& address = GetBluetoothBrailleDisplayAddress();
1356   if (!address.empty()) {
1357     // Maybe start brltty, when we have a bluetooth device stored for
1358     // connection.
1359     RestartBrltty(address);
1360   } else {
1361     // Otherwise, start brltty without an address. This covers cases when
1362     // ChromeVox is toggled off then back on all while a usb braille display is
1363     // connected.
1364     chromeos::UpstartClient::Get()->StartJob(kBrlttyUpstartJobName, {},
1365                                              base::DoNothing());
1366   }
1367 
1368   PlayEarcon(SOUND_SPOKEN_FEEDBACK_ENABLED, PlaySoundOption::ALWAYS);
1369 
1370   extensions::EventRouter* event_router =
1371       extensions::EventRouter::Get(profile_);
1372 
1373   const std::string& extension_id = extension_misc::kChromeVoxExtensionId;
1374 
1375   std::unique_ptr<base::ListValue> event_args =
1376       std::make_unique<base::ListValue>();
1377   std::unique_ptr<extensions::Event> event(new extensions::Event(
1378       extensions::events::ACCESSIBILITY_PRIVATE_ON_INTRODUCE_CHROME_VOX,
1379       extensions::api::accessibility_private::OnIntroduceChromeVox::kEventName,
1380       std::move(event_args)));
1381   event_router->DispatchEventWithLazyListener(extension_id, std::move(event));
1382 
1383   if (!chromevox_panel_) {
1384     chromevox_panel_ = new ChromeVoxPanel(profile_);
1385     chromevox_panel_widget_observer_.reset(new AccessibilityPanelWidgetObserver(
1386         chromevox_panel_->GetWidget(),
1387         base::BindOnce(&AccessibilityManager::OnChromeVoxPanelDestroying,
1388                        base::Unretained(this))));
1389   }
1390 
1391   audio_focus_manager_->SetEnforcementMode(
1392       media_session::mojom::EnforcementMode::kNone);
1393 
1394   InitializeFocusRings(extension_id);
1395 }
1396 
PostUnloadChromeVox()1397 void AccessibilityManager::PostUnloadChromeVox() {
1398   // Do any teardown work needed immediately after ChromeVox actually unloads.
1399   // Stop brltty.
1400   chromeos::UpstartClient::Get()->StopJob(kBrlttyUpstartJobName, {},
1401                                           base::DoNothing());
1402 
1403   PlayEarcon(SOUND_SPOKEN_FEEDBACK_DISABLED, PlaySoundOption::ALWAYS);
1404 
1405   RemoveFocusRings(extension_misc::kChromeVoxExtensionId);
1406 
1407   if (chromevox_panel_) {
1408     chromevox_panel_->Close();
1409     chromevox_panel_ = nullptr;
1410   }
1411 
1412   // In case the user darkened the screen, undarken it now.
1413   SetDarkenScreen(false);
1414 
1415   // Stop speech.
1416   content::TtsController::GetInstance()->Stop();
1417 
1418   audio_focus_manager_->SetEnforcementMode(
1419       media_session::mojom::EnforcementMode::kDefault);
1420 }
1421 
PostSwitchChromeVoxProfile()1422 void AccessibilityManager::PostSwitchChromeVoxProfile() {
1423   if (chromevox_panel_) {
1424     chromevox_panel_->CloseNow();
1425     chromevox_panel_ = nullptr;
1426   }
1427   chromevox_panel_ = new ChromeVoxPanel(profile_);
1428   chromevox_panel_widget_observer_.reset(new AccessibilityPanelWidgetObserver(
1429       chromevox_panel_->GetWidget(),
1430       base::BindOnce(&AccessibilityManager::OnChromeVoxPanelDestroying,
1431                      base::Unretained(this))));
1432 }
1433 
OnChromeVoxPanelDestroying()1434 void AccessibilityManager::OnChromeVoxPanelDestroying() {
1435   chromevox_panel_widget_observer_.reset(nullptr);
1436   chromevox_panel_ = nullptr;
1437 }
1438 
PostLoadSelectToSpeak()1439 void AccessibilityManager::PostLoadSelectToSpeak() {
1440   InitializeFocusRings(extension_misc::kSelectToSpeakExtensionId);
1441 }
1442 
PostUnloadSelectToSpeak()1443 void AccessibilityManager::PostUnloadSelectToSpeak() {
1444   // Do any teardown work needed immediately after Select-to-Speak actually
1445   // unloads.
1446 
1447   // Clear the accessibility focus ring and highlight.
1448   RemoveFocusRings(extension_misc::kSelectToSpeakExtensionId);
1449   HideHighlights();
1450 
1451   // Stop speech.
1452   content::TtsController::GetInstance()->Stop();
1453 }
1454 
PostLoadSwitchAccess()1455 void AccessibilityManager::PostLoadSwitchAccess() {
1456   InitializeFocusRings(extension_misc::kSwitchAccessExtensionId);
1457 }
1458 
PostUnloadSwitchAccess()1459 void AccessibilityManager::PostUnloadSwitchAccess() {
1460   // Do any teardown work needed immediately after Switch Access actually
1461   // unloads.
1462 
1463   // Clear the accessibility focus ring.
1464   RemoveFocusRings(extension_misc::kSwitchAccessExtensionId);
1465 
1466   if (!was_vk_enabled_before_switch_access_) {
1467     ChromeKeyboardControllerClient::Get()->ClearEnableFlag(
1468         keyboard::KeyboardEnableFlag::kExtensionEnabled);
1469   } else {
1470     was_vk_enabled_before_switch_access_ = false;
1471   }
1472 }
1473 
PostLoadAccessibilityCommon()1474 void AccessibilityManager::PostLoadAccessibilityCommon() {
1475   // Do any setup work needed immediately after the Accessibility Common
1476   // extension actually loads. This may be used by all features which make
1477   // use of the Accessibility Common extension.
1478   InitializeFocusRings(extension_misc::kAccessibilityCommonExtensionId);
1479 }
1480 
PostUnloadAccessibilityCommon()1481 void AccessibilityManager::PostUnloadAccessibilityCommon() {
1482   // Do any teardown work needed immediately after the Accessibility Common
1483   // extension actually unloads. This may be used by all features which make
1484   // use of the Accessibility Common extension.
1485   RemoveFocusRings(extension_misc::kAccessibilityCommonExtensionId);
1486 }
1487 
SetKeyboardListenerExtensionId(const std::string & id,content::BrowserContext * context)1488 void AccessibilityManager::SetKeyboardListenerExtensionId(
1489     const std::string& id,
1490     content::BrowserContext* context) {
1491   keyboard_listener_extension_id_ = id;
1492 
1493   extensions::ExtensionRegistry* registry =
1494       extensions::ExtensionRegistry::Get(context);
1495   if (!extension_registry_observer_.IsObserving(registry) && !id.empty())
1496     extension_registry_observer_.Add(registry);
1497 }
1498 
ToggleDictation()1499 bool AccessibilityManager::ToggleDictation() {
1500   if (!profile_)
1501     return false;
1502 
1503   if (!dictation_.get())
1504     dictation_ = std::make_unique<DictationChromeos>(profile_);
1505 
1506   return dictation_->OnToggleDictation();
1507 }
1508 
GetFocusRingId(const std::string & extension_id,const std::string & focus_ring_name)1509 const std::string AccessibilityManager::GetFocusRingId(
1510     const std::string& extension_id,
1511     const std::string& focus_ring_name) {
1512   // Add the focus ring name to the list of focus rings for the extension.
1513   focus_ring_names_for_extension_id_.find(extension_id)
1514       ->second.insert(focus_ring_name);
1515   return extension_id + '-' + focus_ring_name;
1516 }
1517 
InitializeFocusRings(const std::string & extension_id)1518 void AccessibilityManager::InitializeFocusRings(
1519     const std::string& extension_id) {
1520   if (focus_ring_names_for_extension_id_.count(extension_id) == 0) {
1521     focus_ring_names_for_extension_id_.emplace(extension_id,
1522                                                std::set<std::string>());
1523   }
1524 }
1525 
RemoveFocusRings(const std::string & extension_id)1526 void AccessibilityManager::RemoveFocusRings(const std::string& extension_id) {
1527   if (focus_ring_names_for_extension_id_.count(extension_id) != 0) {
1528     const std::set<std::string>& focus_ring_names =
1529         focus_ring_names_for_extension_id_.find(extension_id)->second;
1530 
1531     for (const std::string& focus_ring_name : focus_ring_names)
1532       HideFocusRing(GetFocusRingId(extension_id, focus_ring_name));
1533   }
1534   focus_ring_names_for_extension_id_.erase(extension_id);
1535 }
1536 
SetFocusRing(std::string focus_ring_id,std::unique_ptr<ash::AccessibilityFocusRingInfo> focus_ring)1537 void AccessibilityManager::SetFocusRing(
1538     std::string focus_ring_id,
1539     std::unique_ptr<ash::AccessibilityFocusRingInfo> focus_ring) {
1540   ash::AccessibilityFocusRingController::Get()->SetFocusRing(
1541       focus_ring_id, std::move(focus_ring));
1542 
1543   if (focus_ring_observer_for_test_)
1544     focus_ring_observer_for_test_.Run();
1545 }
1546 
HideFocusRing(std::string focus_ring_id)1547 void AccessibilityManager::HideFocusRing(std::string focus_ring_id) {
1548   ash::AccessibilityFocusRingController::Get()->HideFocusRing(focus_ring_id);
1549   if (focus_ring_observer_for_test_)
1550     focus_ring_observer_for_test_.Run();
1551 }
1552 
SetHighlights(const std::vector<gfx::Rect> & rects_in_screen,SkColor color)1553 void AccessibilityManager::SetHighlights(
1554     const std::vector<gfx::Rect>& rects_in_screen,
1555     SkColor color) {
1556   ash::AccessibilityFocusRingController::Get()->SetHighlights(rects_in_screen,
1557                                                               color);
1558 }
1559 
HideHighlights()1560 void AccessibilityManager::HideHighlights() {
1561   ash::AccessibilityFocusRingController::Get()->HideHighlights();
1562 }
1563 
SetCaretBounds(const gfx::Rect & bounds_in_screen)1564 void AccessibilityManager::SetCaretBounds(const gfx::Rect& bounds_in_screen) {
1565   // For efficiency only send mojo IPCs to ash if the highlight is enabled.
1566   if (!IsCaretHighlightEnabled())
1567     return;
1568 
1569   ash::AccessibilityController::Get()->SetCaretBounds(bounds_in_screen);
1570 
1571   if (caret_bounds_observer_for_test_)
1572     caret_bounds_observer_for_test_.Run(bounds_in_screen);
1573 }
1574 
GetStartupSoundEnabled() const1575 bool AccessibilityManager::GetStartupSoundEnabled() const {
1576   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
1577   const user_manager::UserList& user_list = user_manager->GetUsers();
1578   if (user_list.empty())
1579     return false;
1580 
1581   // |user_list| is sorted by last log in date. Take the most recent user to
1582   // log in.
1583   bool val;
1584   return user_manager::known_user::GetBooleanPref(
1585              user_list[0]->GetAccountId(), kUserStartupSoundEnabled, &val) &&
1586          val;
1587 }
1588 
SetStartupSoundEnabled(bool value) const1589 void AccessibilityManager::SetStartupSoundEnabled(bool value) const {
1590   if (!profile_)
1591     return;
1592 
1593   user_manager::known_user::SetBooleanPref(
1594       multi_user_util::GetAccountIdFromProfile(profile_),
1595       kUserStartupSoundEnabled, value);
1596 }
1597 
GetBluetoothBrailleDisplayAddress() const1598 const std::string AccessibilityManager::GetBluetoothBrailleDisplayAddress()
1599     const {
1600   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
1601   const user_manager::UserList& user_list = user_manager->GetUsers();
1602   if (user_list.empty())
1603     return std::string();
1604 
1605   // |user_list| is sorted by last log in date. Take the most recent user to
1606   // log in.
1607   std::string val;
1608   return user_manager::known_user::GetStringPref(
1609              user_list[0]->GetAccountId(), kUserBluetoothBrailleDisplayAddress,
1610              &val)
1611              ? val
1612              : std::string();
1613 }
1614 
UpdateBluetoothBrailleDisplayAddress(const std::string & address)1615 void AccessibilityManager::UpdateBluetoothBrailleDisplayAddress(
1616     const std::string& address) {
1617   CHECK(spoken_feedback_enabled_);
1618   if (!profile_)
1619     return;
1620 
1621   user_manager::known_user::SetStringPref(
1622       multi_user_util::GetAccountIdFromProfile(profile_),
1623       kUserBluetoothBrailleDisplayAddress, address);
1624   RestartBrltty(address);
1625 }
1626 
SetProfileForTest(Profile * profile)1627 void AccessibilityManager::SetProfileForTest(Profile* profile) {
1628   SetProfile(profile);
1629 }
1630 
1631 // static
SetBrailleControllerForTest(BrailleController * controller)1632 void AccessibilityManager::SetBrailleControllerForTest(
1633     BrailleController* controller) {
1634   g_braille_controller_for_test = controller;
1635 }
1636 
SetFocusRingObserverForTest(base::RepeatingCallback<void ()> observer)1637 void AccessibilityManager::SetFocusRingObserverForTest(
1638     base::RepeatingCallback<void()> observer) {
1639   focus_ring_observer_for_test_ = observer;
1640 }
1641 
SetSelectToSpeakStateObserverForTest(base::RepeatingCallback<void ()> observer)1642 void AccessibilityManager::SetSelectToSpeakStateObserverForTest(
1643     base::RepeatingCallback<void()> observer) {
1644   select_to_speak_state_observer_for_test_ = observer;
1645 }
1646 
SetCaretBoundsObserverForTest(base::RepeatingCallback<void (const gfx::Rect &)> observer)1647 void AccessibilityManager::SetCaretBoundsObserverForTest(
1648     base::RepeatingCallback<void(const gfx::Rect&)> observer) {
1649   caret_bounds_observer_for_test_ = observer;
1650 }
1651 
SetSwitchAccessKeysForTest(const std::vector<int> & keys)1652 void AccessibilityManager::SetSwitchAccessKeysForTest(
1653     const std::vector<int>& keys) {
1654   // Sets all keys to "Select" command.
1655   ListPrefUpdate update(profile_->GetPrefs(),
1656                         ash::prefs::kAccessibilitySwitchAccessSelectKeyCodes);
1657   for (int key : keys)
1658     update->AppendInteger(key);
1659 
1660   profile_->GetPrefs()->CommitPendingWrite();
1661 }
1662 
1663 }  // namespace chromeos
1664