• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "ash/audio/sounds.h"
8 #include "ash/autoclick/autoclick_controller.h"
9 #include "ash/high_contrast/high_contrast_controller.h"
10 #include "ash/metrics/user_metrics_recorder.h"
11 #include "ash/session/session_state_delegate.h"
12 #include "ash/shell.h"
13 #include "ash/sticky_keys/sticky_keys_controller.h"
14 #include "ash/system/tray/system_tray_notifier.h"
15 #include "base/callback.h"
16 #include "base/callback_helpers.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/memory/singleton.h"
19 #include "base/metrics/histogram.h"
20 #include "base/path_service.h"
21 #include "base/prefs/pref_member.h"
22 #include "base/prefs/pref_service.h"
23 #include "base/strings/string_split.h"
24 #include "base/strings/string_util.h"
25 #include "base/time/time.h"
26 #include "base/values.h"
27 #include "chrome/browser/accessibility/accessibility_extension_api.h"
28 #include "chrome/browser/browser_process.h"
29 #include "chrome/browser/chrome_notification_types.h"
30 #include "chrome/browser/chromeos/accessibility/magnification_manager.h"
31 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
32 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
33 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
34 #include "chrome/browser/chromeos/login/ui/webui_login_view.h"
35 #include "chrome/browser/chromeos/profiles/profile_helper.h"
36 #include "chrome/browser/chromeos/ui/accessibility_focus_ring_controller.h"
37 #include "chrome/browser/extensions/component_loader.h"
38 #include "chrome/browser/extensions/extension_service.h"
39 #include "chrome/browser/profiles/profile.h"
40 #include "chrome/browser/profiles/profile_manager.h"
41 #include "chrome/common/chrome_paths.h"
42 #include "chrome/common/extensions/api/accessibility_private.h"
43 #include "chrome/common/extensions/extension_constants.h"
44 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
45 #include "chrome/common/pref_names.h"
46 #include "chrome/grit/browser_resources.h"
47 #include "chromeos/audio/chromeos_sounds.h"
48 #include "chromeos/ime/input_method_manager.h"
49 #include "chromeos/login/login_state.h"
50 #include "components/user_manager/user_manager.h"
51 #include "content/public/browser/browser_accessibility_state.h"
52 #include "content/public/browser/browser_thread.h"
53 #include "content/public/browser/notification_details.h"
54 #include "content/public/browser/notification_service.h"
55 #include "content/public/browser/notification_source.h"
56 #include "content/public/browser/render_process_host.h"
57 #include "content/public/browser/render_view_host.h"
58 #include "content/public/browser/web_contents.h"
59 #include "content/public/browser/web_ui.h"
60 #include "extensions/browser/extension_system.h"
61 #include "extensions/browser/file_reader.h"
62 #include "extensions/common/extension.h"
63 #include "extensions/common/extension_messages.h"
64 #include "extensions/common/extension_resource.h"
65 #include "media/audio/sounds/sounds_manager.h"
66 #include "ui/base/resource/resource_bundle.h"
67 #include "ui/keyboard/keyboard_controller.h"
68 #include "ui/keyboard/keyboard_util.h"
69 
70 using content::BrowserThread;
71 using content::RenderViewHost;
72 using extensions::api::braille_display_private::BrailleController;
73 using extensions::api::braille_display_private::DisplayState;
74 using extensions::api::braille_display_private::KeyEvent;
75 
76 namespace chromeos {
77 
78 namespace {
79 
80 static chromeos::AccessibilityManager* g_accessibility_manager = NULL;
81 
82 static BrailleController* g_braille_controller_for_test = NULL;
83 
GetBrailleController()84 BrailleController* GetBrailleController() {
85   return g_braille_controller_for_test
86       ? g_braille_controller_for_test
87       : BrailleController::GetInstance();
88 }
89 
GetChromeVoxPath()90 base::FilePath GetChromeVoxPath() {
91   base::FilePath path;
92   if (!PathService::Get(chrome::DIR_RESOURCES, &path))
93     NOTREACHED();
94   path = path.Append(extension_misc::kChromeVoxExtensionPath);
95   return path;
96 }
97 
98 // Helper class that directly loads an extension's content scripts into
99 // all of the frames corresponding to a given RenderViewHost.
100 class ContentScriptLoader {
101  public:
102   // Initialize the ContentScriptLoader with the ID of the extension
103   // and the RenderViewHost where the scripts should be loaded.
ContentScriptLoader(const std::string & extension_id,int render_process_id,int render_view_id)104   ContentScriptLoader(const std::string& extension_id,
105                       int render_process_id,
106                       int render_view_id)
107       : extension_id_(extension_id),
108         render_process_id_(render_process_id),
109         render_view_id_(render_view_id) {}
110 
111   // Call this once with the ExtensionResource corresponding to each
112   // content script to be loaded.
AppendScript(extensions::ExtensionResource resource)113   void AppendScript(extensions::ExtensionResource resource) {
114     resources_.push(resource);
115   }
116 
117   // Finally, call this method once to fetch all of the resources and
118   // load them. This method will delete this object when done.
Run()119   void Run() {
120     if (resources_.empty()) {
121       delete this;
122       return;
123     }
124 
125     extensions::ExtensionResource resource = resources_.front();
126     resources_.pop();
127     scoped_refptr<FileReader> reader(new FileReader(resource, base::Bind(
128         &ContentScriptLoader::OnFileLoaded, base::Unretained(this))));
129     reader->Start();
130   }
131 
132  private:
OnFileLoaded(bool success,const std::string & data)133   void OnFileLoaded(bool success, const std::string& data) {
134     if (success) {
135       ExtensionMsg_ExecuteCode_Params params;
136       params.request_id = 0;
137       params.extension_id = extension_id_;
138       params.is_javascript = true;
139       params.code = data;
140       params.run_at = extensions::UserScript::DOCUMENT_IDLE;
141       params.all_frames = true;
142       params.match_about_blank = false;
143       params.in_main_world = false;
144 
145       RenderViewHost* render_view_host =
146           RenderViewHost::FromID(render_process_id_, render_view_id_);
147       if (render_view_host) {
148         render_view_host->Send(new ExtensionMsg_ExecuteCode(
149             render_view_host->GetRoutingID(), params));
150       }
151     }
152     Run();
153   }
154 
155   std::string extension_id_;
156   int render_process_id_;
157   int render_view_id_;
158   std::queue<extensions::ExtensionResource> resources_;
159 };
160 
161 void InjectChromeVoxContentScript(
162     ExtensionService* extension_service,
163     int render_process_id,
164     int render_view_id,
165     const base::Closure& done_cb);
166 
LoadChromeVoxExtension(Profile * profile,RenderViewHost * render_view_host,base::Closure done_cb)167 void LoadChromeVoxExtension(
168     Profile* profile,
169     RenderViewHost* render_view_host,
170     base::Closure done_cb) {
171   ExtensionService* extension_service =
172       extensions::ExtensionSystem::Get(profile)->extension_service();
173   if (render_view_host) {
174     // Wrap the passed in callback to inject the content script.
175     done_cb = base::Bind(
176         &InjectChromeVoxContentScript,
177         extension_service,
178         render_view_host->GetProcess()->GetID(),
179         render_view_host->GetRoutingID(),
180         done_cb);
181   }
182   extension_service->component_loader()->AddChromeVoxExtension(done_cb);
183 }
184 
InjectChromeVoxContentScript(ExtensionService * extension_service,int render_process_id,int render_view_id,const base::Closure & done_cb)185 void InjectChromeVoxContentScript(
186     ExtensionService* extension_service,
187     int render_process_id,
188     int render_view_id,
189     const base::Closure& done_cb) {
190   // Make sure to always run |done_cb|.  ChromeVox was loaded even if we end up
191   // not injecting into this particular render view.
192   base::ScopedClosureRunner done_runner(done_cb);
193   RenderViewHost* render_view_host =
194       RenderViewHost::FromID(render_process_id, render_view_id);
195   if (!render_view_host)
196     return;
197   const extensions::Extension* extension =
198       extension_service->extensions()->GetByID(
199           extension_misc::kChromeVoxExtensionId);
200 
201   // Set a flag to tell ChromeVox that it's just been enabled,
202   // so that it won't interrupt our speech feedback enabled message.
203   ExtensionMsg_ExecuteCode_Params params;
204   params.request_id = 0;
205   params.extension_id = extension->id();
206   params.is_javascript = true;
207   params.code = "window.INJECTED_AFTER_LOAD = true;";
208   params.run_at = extensions::UserScript::DOCUMENT_IDLE;
209   params.all_frames = true;
210   params.match_about_blank = false;
211   params.in_main_world = false;
212   render_view_host->Send(new ExtensionMsg_ExecuteCode(
213       render_view_host->GetRoutingID(), params));
214 
215   // Inject ChromeVox' content scripts.
216   ContentScriptLoader* loader = new ContentScriptLoader(
217       extension->id(), render_view_host->GetProcess()->GetID(),
218       render_view_host->GetRoutingID());
219 
220   const extensions::UserScriptList& content_scripts =
221       extensions::ContentScriptsInfo::GetContentScripts(extension);
222   for (size_t i = 0; i < content_scripts.size(); i++) {
223     const extensions::UserScript& script = content_scripts[i];
224     for (size_t j = 0; j < script.js_scripts().size(); ++j) {
225       const extensions::UserScript::File& file = script.js_scripts()[j];
226       extensions::ExtensionResource resource = extension->GetResource(
227           file.relative_path());
228       loader->AppendScript(resource);
229     }
230   }
231   loader->Run();  // It cleans itself up when done.
232 }
233 
UnloadChromeVoxExtension(Profile * profile)234 void UnloadChromeVoxExtension(Profile* profile) {
235   base::FilePath path = GetChromeVoxPath();
236   ExtensionService* extension_service =
237       extensions::ExtensionSystem::Get(profile)->extension_service();
238   extension_service->component_loader()->Remove(path);
239 }
240 
241 }  // namespace
242 
243 ///////////////////////////////////////////////////////////////////////////////
244 // AccessibilityStatusEventDetails
245 
AccessibilityStatusEventDetails(AccessibilityNotificationType notification_type,bool enabled,ash::AccessibilityNotificationVisibility notify)246 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
247     AccessibilityNotificationType notification_type,
248     bool enabled,
249     ash::AccessibilityNotificationVisibility notify)
250   : notification_type(notification_type),
251     enabled(enabled),
252     magnifier_type(ash::kDefaultMagnifierType),
253     notify(notify) {}
254 
AccessibilityStatusEventDetails(AccessibilityNotificationType notification_type,bool enabled,ash::MagnifierType magnifier_type,ash::AccessibilityNotificationVisibility notify)255 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
256     AccessibilityNotificationType notification_type,
257     bool enabled,
258     ash::MagnifierType magnifier_type,
259     ash::AccessibilityNotificationVisibility notify)
260   : notification_type(notification_type),
261     enabled(enabled),
262     magnifier_type(magnifier_type),
263     notify(notify) {}
264 
265 ///////////////////////////////////////////////////////////////////////////////
266 //
267 // AccessibilityManager::PrefHandler
268 
PrefHandler(const char * pref_path)269 AccessibilityManager::PrefHandler::PrefHandler(const char* pref_path)
270     : pref_path_(pref_path) {}
271 
~PrefHandler()272 AccessibilityManager::PrefHandler::~PrefHandler() {}
273 
HandleProfileChanged(Profile * previous_profile,Profile * current_profile)274 void AccessibilityManager::PrefHandler::HandleProfileChanged(
275     Profile* previous_profile, Profile* current_profile) {
276   // Returns if the current profile is null.
277   if (!current_profile)
278     return;
279 
280   // If the user set a pref value on the login screen and is now starting a
281   // session with a new profile, copy the pref value to the profile.
282   if ((previous_profile &&
283        ProfileHelper::IsSigninProfile(previous_profile) &&
284        current_profile->IsNewProfile() &&
285        !ProfileHelper::IsSigninProfile(current_profile)) ||
286       // Special case for Guest mode:
287       // Guest mode launches a guest-mode browser process before session starts,
288       // so the previous profile is null.
289       (!previous_profile &&
290        current_profile->IsGuestSession())) {
291     // Returns if the pref has not been set by the user.
292     const PrefService::Preference* pref = ProfileHelper::GetSigninProfile()->
293         GetPrefs()->FindPreference(pref_path_);
294     if (!pref || !pref->IsUserControlled())
295       return;
296 
297     // Copy the pref value from the signin screen.
298     const base::Value* value_on_login = pref->GetValue();
299     PrefService* user_prefs = current_profile->GetPrefs();
300     user_prefs->Set(pref_path_, *value_on_login);
301   }
302 }
303 
304 ///////////////////////////////////////////////////////////////////////////////
305 //
306 // AccessibilityManager
307 
308 // static
Initialize()309 void AccessibilityManager::Initialize() {
310   CHECK(g_accessibility_manager == NULL);
311   g_accessibility_manager = new AccessibilityManager();
312 }
313 
314 // static
Shutdown()315 void AccessibilityManager::Shutdown() {
316   CHECK(g_accessibility_manager);
317   delete g_accessibility_manager;
318   g_accessibility_manager = NULL;
319 }
320 
321 // static
Get()322 AccessibilityManager* AccessibilityManager::Get() {
323   return g_accessibility_manager;
324 }
325 
AccessibilityManager()326 AccessibilityManager::AccessibilityManager()
327     : profile_(NULL),
328       chrome_vox_loaded_on_lock_screen_(false),
329       chrome_vox_loaded_on_user_screen_(false),
330       large_cursor_pref_handler_(prefs::kAccessibilityLargeCursorEnabled),
331       spoken_feedback_pref_handler_(prefs::kAccessibilitySpokenFeedbackEnabled),
332       high_contrast_pref_handler_(prefs::kAccessibilityHighContrastEnabled),
333       autoclick_pref_handler_(prefs::kAccessibilityAutoclickEnabled),
334       autoclick_delay_pref_handler_(prefs::kAccessibilityAutoclickDelayMs),
335       virtual_keyboard_pref_handler_(
336           prefs::kAccessibilityVirtualKeyboardEnabled),
337       large_cursor_enabled_(false),
338       sticky_keys_enabled_(false),
339       spoken_feedback_enabled_(false),
340       high_contrast_enabled_(false),
341       autoclick_enabled_(false),
342       autoclick_delay_ms_(ash::AutoclickController::kDefaultAutoclickDelayMs),
343       virtual_keyboard_enabled_(false),
344       spoken_feedback_notification_(ash::A11Y_NOTIFICATION_NONE),
345       should_speak_chrome_vox_announcements_on_user_screen_(true),
346       system_sounds_enabled_(false),
347       braille_display_connected_(false),
348       scoped_braille_observer_(this),
349       braille_ime_current_(false),
350       weak_ptr_factory_(this) {
351   notification_registrar_.Add(this,
352                               chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
353                               content::NotificationService::AllSources());
354   notification_registrar_.Add(this,
355                               chrome::NOTIFICATION_SESSION_STARTED,
356                               content::NotificationService::AllSources());
357   notification_registrar_.Add(this,
358                               chrome::NOTIFICATION_PROFILE_DESTROYED,
359                               content::NotificationService::AllSources());
360   notification_registrar_.Add(this,
361                               chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED,
362                               content::NotificationService::AllSources());
363 
364   input_method::InputMethodManager::Get()->AddObserver(this);
365 
366   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
367   media::SoundsManager* manager = media::SoundsManager::Get();
368   manager->Initialize(SOUND_SHUTDOWN,
369                       bundle.GetRawDataResource(IDR_SOUND_SHUTDOWN_WAV));
370   manager->Initialize(
371       SOUND_SPOKEN_FEEDBACK_ENABLED,
372       bundle.GetRawDataResource(IDR_SOUND_SPOKEN_FEEDBACK_ENABLED_WAV));
373   manager->Initialize(
374       SOUND_SPOKEN_FEEDBACK_DISABLED,
375       bundle.GetRawDataResource(IDR_SOUND_SPOKEN_FEEDBACK_DISABLED_WAV));
376   manager->Initialize(SOUND_PASSTHROUGH,
377                       bundle.GetRawDataResource(IDR_SOUND_PASSTHROUGH_WAV));
378   manager->Initialize(SOUND_EXIT_SCREEN,
379                       bundle.GetRawDataResource(IDR_SOUND_EXIT_SCREEN_WAV));
380   manager->Initialize(SOUND_ENTER_SCREEN,
381                       bundle.GetRawDataResource(IDR_SOUND_ENTER_SCREEN_WAV));
382 }
383 
~AccessibilityManager()384 AccessibilityManager::~AccessibilityManager() {
385   CHECK(this == g_accessibility_manager);
386   AccessibilityStatusEventDetails details(
387       ACCESSIBILITY_MANAGER_SHUTDOWN,
388       false,
389       ash::A11Y_NOTIFICATION_NONE);
390   NotifyAccessibilityStatusChanged(details);
391   input_method::InputMethodManager::Get()->RemoveObserver(this);
392 }
393 
ShouldShowAccessibilityMenu()394 bool AccessibilityManager::ShouldShowAccessibilityMenu() {
395   // If any of the loaded profiles has an accessibility feature turned on - or
396   // enforced to always show the menu - we return true to show the menu.
397   std::vector<Profile*> profiles =
398       g_browser_process->profile_manager()->GetLoadedProfiles();
399   for (std::vector<Profile*>::iterator it = profiles.begin();
400        it != profiles.end();
401        ++it) {
402     PrefService* pref_service = (*it)->GetPrefs();
403     if (pref_service->GetBoolean(prefs::kAccessibilityStickyKeysEnabled) ||
404         pref_service->GetBoolean(prefs::kAccessibilityLargeCursorEnabled) ||
405         pref_service->GetBoolean(prefs::kAccessibilitySpokenFeedbackEnabled) ||
406         pref_service->GetBoolean(prefs::kAccessibilityHighContrastEnabled) ||
407         pref_service->GetBoolean(prefs::kAccessibilityAutoclickEnabled) ||
408         pref_service->GetBoolean(prefs::kShouldAlwaysShowAccessibilityMenu) ||
409         pref_service->GetBoolean(prefs::kAccessibilityScreenMagnifierEnabled) ||
410         pref_service->GetBoolean(prefs::kAccessibilityVirtualKeyboardEnabled))
411       return true;
412   }
413   return false;
414 }
415 
ShouldEnableCursorCompositing()416 bool AccessibilityManager::ShouldEnableCursorCompositing() {
417 #if defined(OS_CHROMEOS)
418   if (!profile_)
419     return false;
420   PrefService* pref_service = profile_->GetPrefs();
421   // Enable cursor compositing when one or more of the listed accessibility
422   // features are turned on.
423   if (pref_service->GetBoolean(prefs::kAccessibilityLargeCursorEnabled) ||
424       pref_service->GetBoolean(prefs::kAccessibilityHighContrastEnabled) ||
425       pref_service->GetBoolean(prefs::kAccessibilityScreenMagnifierEnabled))
426     return true;
427 #endif
428   return false;
429 }
430 
EnableLargeCursor(bool enabled)431 void AccessibilityManager::EnableLargeCursor(bool enabled) {
432   if (!profile_)
433     return;
434 
435   PrefService* pref_service = profile_->GetPrefs();
436   pref_service->SetBoolean(prefs::kAccessibilityLargeCursorEnabled, enabled);
437   pref_service->CommitPendingWrite();
438 }
439 
UpdateLargeCursorFromPref()440 void AccessibilityManager::UpdateLargeCursorFromPref() {
441   if (!profile_)
442     return;
443 
444   const bool enabled =
445       profile_->GetPrefs()->GetBoolean(prefs::kAccessibilityLargeCursorEnabled);
446 
447   if (large_cursor_enabled_ == enabled)
448     return;
449 
450   large_cursor_enabled_ = enabled;
451 
452   AccessibilityStatusEventDetails details(
453       ACCESSIBILITY_TOGGLE_LARGE_CURSOR,
454       enabled,
455       ash::A11Y_NOTIFICATION_NONE);
456 
457   NotifyAccessibilityStatusChanged(details);
458 #if !defined(USE_ATHENA)
459   // crbug.com/408733 (and for all USE_ATHENA in this file)
460 
461 #if defined(USE_ASH)
462   // Large cursor is implemented only in ash.
463   ash::Shell::GetInstance()->cursor_manager()->SetCursorSet(
464       enabled ? ui::CURSOR_SET_LARGE : ui::CURSOR_SET_NORMAL);
465 #endif
466 
467 #if defined(OS_CHROMEOS)
468   ash::Shell::GetInstance()->SetCursorCompositingEnabled(
469       ShouldEnableCursorCompositing());
470 #endif
471 
472 #endif // !USE_ATHENA
473 }
474 
IsIncognitoAllowed()475 bool AccessibilityManager::IsIncognitoAllowed() {
476   // Supervised users can't create incognito-mode windows.
477   return !(user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser());
478 }
479 
IsLargeCursorEnabled()480 bool AccessibilityManager::IsLargeCursorEnabled() {
481   return large_cursor_enabled_;
482 }
483 
EnableStickyKeys(bool enabled)484 void AccessibilityManager::EnableStickyKeys(bool enabled) {
485   if (!profile_)
486     return;
487   PrefService* pref_service = profile_->GetPrefs();
488   pref_service->SetBoolean(prefs::kAccessibilityStickyKeysEnabled, enabled);
489   pref_service->CommitPendingWrite();
490 }
491 
IsStickyKeysEnabled()492 bool AccessibilityManager::IsStickyKeysEnabled() {
493   return sticky_keys_enabled_;
494 }
495 
UpdateStickyKeysFromPref()496 void AccessibilityManager::UpdateStickyKeysFromPref() {
497   if (!profile_)
498     return;
499 
500   const bool enabled =
501       profile_->GetPrefs()->GetBoolean(prefs::kAccessibilityStickyKeysEnabled);
502 
503   if (sticky_keys_enabled_ == enabled)
504     return;
505 
506   sticky_keys_enabled_ = enabled;
507 #if defined(USE_ASH) && !defined(USE_ATHENA)
508   ash::Shell::GetInstance()->sticky_keys_controller()->Enable(enabled);
509 #endif
510 }
511 
EnableSpokenFeedback(bool enabled,ash::AccessibilityNotificationVisibility notify)512 void AccessibilityManager::EnableSpokenFeedback(
513     bool enabled,
514     ash::AccessibilityNotificationVisibility notify) {
515   if (!profile_)
516     return;
517 #if !defined(USE_ATHENA)
518   ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(
519       enabled ? ash::UMA_STATUS_AREA_ENABLE_SPOKEN_FEEDBACK
520               : ash::UMA_STATUS_AREA_DISABLE_SPOKEN_FEEDBACK);
521 #endif
522 
523   spoken_feedback_notification_ = notify;
524 
525   PrefService* pref_service = profile_->GetPrefs();
526   pref_service->SetBoolean(prefs::kAccessibilitySpokenFeedbackEnabled, enabled);
527   pref_service->CommitPendingWrite();
528 
529   spoken_feedback_notification_ = ash::A11Y_NOTIFICATION_NONE;
530 }
531 
UpdateSpokenFeedbackFromPref()532 void AccessibilityManager::UpdateSpokenFeedbackFromPref() {
533   if (!profile_)
534     return;
535 
536   const bool enabled = profile_->GetPrefs()->GetBoolean(
537       prefs::kAccessibilitySpokenFeedbackEnabled);
538 
539   if (spoken_feedback_enabled_ == enabled)
540     return;
541 
542   spoken_feedback_enabled_ = enabled;
543 
544   ExtensionAccessibilityEventRouter::GetInstance()->
545       SetAccessibilityEnabled(enabled);
546 
547   AccessibilityStatusEventDetails details(
548       ACCESSIBILITY_TOGGLE_SPOKEN_FEEDBACK,
549       enabled,
550       spoken_feedback_notification_);
551 
552   NotifyAccessibilityStatusChanged(details);
553 
554   if (enabled) {
555     LoadChromeVox();
556   } else {
557     UnloadChromeVox();
558   }
559   UpdateBrailleImeState();
560 }
561 
LoadChromeVox()562 void AccessibilityManager::LoadChromeVox() {
563   base::Closure done_cb = base::Bind(&AccessibilityManager::PostLoadChromeVox,
564                                      weak_ptr_factory_.GetWeakPtr(),
565                                      profile_);
566   ScreenLocker* screen_locker = ScreenLocker::default_screen_locker();
567   if (screen_locker && screen_locker->locked()) {
568     // If on the lock screen, loads ChromeVox only to the lock screen as for
569     // now. On unlock, it will be loaded to the user screen.
570     // (see. AccessibilityManager::Observe())
571     LoadChromeVoxToLockScreen(done_cb);
572   } else {
573     LoadChromeVoxToUserScreen(done_cb);
574   }
575 }
576 
LoadChromeVoxToUserScreen(const base::Closure & done_cb)577 void AccessibilityManager::LoadChromeVoxToUserScreen(
578     const base::Closure& done_cb) {
579   if (chrome_vox_loaded_on_user_screen_)
580     return;
581 
582   // Determine whether an OOBE screen is currently being shown. If so,
583   // ChromeVox will be injected directly into that screen.
584   content::WebUI* login_web_ui = NULL;
585 
586   if (ProfileHelper::IsSigninProfile(profile_)) {
587     LoginDisplayHost* login_display_host = LoginDisplayHostImpl::default_host();
588     if (login_display_host) {
589       WebUILoginView* web_ui_login_view =
590           login_display_host->GetWebUILoginView();
591       if (web_ui_login_view)
592         login_web_ui = web_ui_login_view->GetWebUI();
593     }
594 
595     // Lock screen uses the signin progile.
596     chrome_vox_loaded_on_lock_screen_ = true;
597   }
598 
599   chrome_vox_loaded_on_user_screen_ = true;
600   LoadChromeVoxExtension(
601       profile_, login_web_ui ?
602       login_web_ui->GetWebContents()->GetRenderViewHost() : NULL,
603       done_cb);
604 }
605 
LoadChromeVoxToLockScreen(const base::Closure & done_cb)606 void AccessibilityManager::LoadChromeVoxToLockScreen(
607     const base::Closure& done_cb) {
608   if (chrome_vox_loaded_on_lock_screen_)
609     return;
610 
611   ScreenLocker* screen_locker = ScreenLocker::default_screen_locker();
612   if (screen_locker && screen_locker->locked()) {
613     content::WebUI* lock_web_ui = screen_locker->GetAssociatedWebUI();
614     if (lock_web_ui) {
615       Profile* profile = Profile::FromWebUI(lock_web_ui);
616       chrome_vox_loaded_on_lock_screen_ = true;
617       LoadChromeVoxExtension(
618           profile,
619           lock_web_ui->GetWebContents()->GetRenderViewHost(),
620           done_cb);
621     }
622   }
623 }
624 
UnloadChromeVox()625 void AccessibilityManager::UnloadChromeVox() {
626   if (chrome_vox_loaded_on_lock_screen_)
627     UnloadChromeVoxFromLockScreen();
628 
629   if (chrome_vox_loaded_on_user_screen_) {
630     UnloadChromeVoxExtension(profile_);
631     chrome_vox_loaded_on_user_screen_ = false;
632   }
633 
634   PostUnloadChromeVox(profile_);
635 }
636 
UnloadChromeVoxFromLockScreen()637 void AccessibilityManager::UnloadChromeVoxFromLockScreen() {
638   // Lock screen uses the signin progile.
639   Profile* signin_profile = ProfileHelper::GetSigninProfile();
640   UnloadChromeVoxExtension(signin_profile);
641   chrome_vox_loaded_on_lock_screen_ = false;
642 }
643 
IsSpokenFeedbackEnabled()644 bool AccessibilityManager::IsSpokenFeedbackEnabled() {
645   return spoken_feedback_enabled_;
646 }
647 
ToggleSpokenFeedback(ash::AccessibilityNotificationVisibility notify)648 void AccessibilityManager::ToggleSpokenFeedback(
649     ash::AccessibilityNotificationVisibility notify) {
650   EnableSpokenFeedback(!IsSpokenFeedbackEnabled(), notify);
651 }
652 
EnableHighContrast(bool enabled)653 void AccessibilityManager::EnableHighContrast(bool enabled) {
654   if (!profile_)
655     return;
656 
657   PrefService* pref_service = profile_->GetPrefs();
658   pref_service->SetBoolean(prefs::kAccessibilityHighContrastEnabled, enabled);
659   pref_service->CommitPendingWrite();
660 }
661 
UpdateHighContrastFromPref()662 void AccessibilityManager::UpdateHighContrastFromPref() {
663   if (!profile_)
664     return;
665 
666   const bool enabled = profile_->GetPrefs()->GetBoolean(
667       prefs::kAccessibilityHighContrastEnabled);
668 
669   if (high_contrast_enabled_ == enabled)
670     return;
671 
672   high_contrast_enabled_ = enabled;
673 
674   AccessibilityStatusEventDetails details(
675       ACCESSIBILITY_TOGGLE_HIGH_CONTRAST_MODE,
676       enabled,
677       ash::A11Y_NOTIFICATION_NONE);
678 
679   NotifyAccessibilityStatusChanged(details);
680 
681 #if !defined(USE_ATHENA)
682 
683 #if defined(USE_ASH)
684   ash::Shell::GetInstance()->high_contrast_controller()->SetEnabled(enabled);
685 #endif
686 
687 #if defined(OS_CHROMEOS)
688   ash::Shell::GetInstance()->SetCursorCompositingEnabled(
689       ShouldEnableCursorCompositing());
690 #endif
691 
692 #endif
693 }
694 
OnLocaleChanged()695 void AccessibilityManager::OnLocaleChanged() {
696   if (!profile_)
697     return;
698 
699   if (!IsSpokenFeedbackEnabled())
700     return;
701 
702   // If the system locale changes and spoken feedback is enabled,
703   // reload ChromeVox so that it switches its internal translations
704   // to the new language.
705   EnableSpokenFeedback(false, ash::A11Y_NOTIFICATION_NONE);
706   EnableSpokenFeedback(true, ash::A11Y_NOTIFICATION_NONE);
707 }
708 
PlayEarcon(int sound_key)709 void AccessibilityManager::PlayEarcon(int sound_key) {
710   DCHECK(sound_key < chromeos::SOUND_COUNT);
711   ash::PlaySystemSoundIfSpokenFeedback(sound_key);
712 }
713 
IsHighContrastEnabled()714 bool AccessibilityManager::IsHighContrastEnabled() {
715   return high_contrast_enabled_;
716 }
717 
EnableAutoclick(bool enabled)718 void AccessibilityManager::EnableAutoclick(bool enabled) {
719   if (!profile_)
720     return;
721 
722   PrefService* pref_service = profile_->GetPrefs();
723   pref_service->SetBoolean(prefs::kAccessibilityAutoclickEnabled, enabled);
724   pref_service->CommitPendingWrite();
725 }
726 
IsAutoclickEnabled()727 bool AccessibilityManager::IsAutoclickEnabled() {
728   return autoclick_enabled_;
729 }
730 
UpdateAutoclickFromPref()731 void AccessibilityManager::UpdateAutoclickFromPref() {
732   bool enabled =
733       profile_->GetPrefs()->GetBoolean(prefs::kAccessibilityAutoclickEnabled);
734 
735   if (autoclick_enabled_ == enabled)
736     return;
737   autoclick_enabled_ = enabled;
738 
739 #if defined(USE_ASH) && !defined(USE_ATHENA)
740   ash::Shell::GetInstance()->autoclick_controller()->SetEnabled(enabled);
741 #endif
742 }
743 
SetAutoclickDelay(int delay_ms)744 void AccessibilityManager::SetAutoclickDelay(int delay_ms) {
745   if (!profile_)
746     return;
747 
748   PrefService* pref_service = profile_->GetPrefs();
749   pref_service->SetInteger(prefs::kAccessibilityAutoclickDelayMs, delay_ms);
750   pref_service->CommitPendingWrite();
751 }
752 
GetAutoclickDelay() const753 int AccessibilityManager::GetAutoclickDelay() const {
754   return autoclick_delay_ms_;
755 }
756 
UpdateAutoclickDelayFromPref()757 void AccessibilityManager::UpdateAutoclickDelayFromPref() {
758   int autoclick_delay_ms =
759       profile_->GetPrefs()->GetInteger(prefs::kAccessibilityAutoclickDelayMs);
760 
761   if (autoclick_delay_ms == autoclick_delay_ms_)
762     return;
763   autoclick_delay_ms_ = autoclick_delay_ms;
764 
765 #if defined(USE_ASH) && !defined(USE_ATHENA)
766   ash::Shell::GetInstance()->autoclick_controller()->SetAutoclickDelay(
767       autoclick_delay_ms_);
768 #endif
769 }
770 
EnableVirtualKeyboard(bool enabled)771 void AccessibilityManager::EnableVirtualKeyboard(bool enabled) {
772   if (!profile_)
773     return;
774 
775   PrefService* pref_service = profile_->GetPrefs();
776   pref_service->SetBoolean(prefs::kAccessibilityVirtualKeyboardEnabled,
777                            enabled);
778   pref_service->CommitPendingWrite();
779 }
780 
IsVirtualKeyboardEnabled()781 bool AccessibilityManager::IsVirtualKeyboardEnabled() {
782   return virtual_keyboard_enabled_;
783 }
784 
UpdateVirtualKeyboardFromPref()785 void AccessibilityManager::UpdateVirtualKeyboardFromPref() {
786   if (!profile_)
787     return;
788 
789   const bool enabled = profile_->GetPrefs()->GetBoolean(
790       prefs::kAccessibilityVirtualKeyboardEnabled);
791 
792   if (virtual_keyboard_enabled_ == enabled)
793     return;
794   virtual_keyboard_enabled_ = enabled;
795 
796   AccessibilityStatusEventDetails details(
797       ACCESSIBILITY_TOGGLE_VIRTUAL_KEYBOARD,
798       enabled,
799       ash::A11Y_NOTIFICATION_NONE);
800 
801   NotifyAccessibilityStatusChanged(details);
802 
803 #if defined(USE_ASH) && !defined(USE_ATHENA)
804   keyboard::SetAccessibilityKeyboardEnabled(enabled);
805   // Note that there are two versions of the on-screen keyboard. A full layout
806   // is provided for accessibility, which includes sticky modifier keys to
807   // enable typing of hotkeys. A compact version is used in touchview mode
808   // to provide a layout with larger keys to facilitate touch typing. In the
809   // event that the a11y keyboard is being disabled, an on-screen keyboard might
810   // still be enabled and a forced reset is required to pick up the layout
811   // change.
812   if (keyboard::IsKeyboardEnabled())
813     ash::Shell::GetInstance()->CreateKeyboard();
814   else
815     ash::Shell::GetInstance()->DeactivateKeyboard();
816 #endif
817 }
818 
IsBrailleDisplayConnected() const819 bool AccessibilityManager::IsBrailleDisplayConnected() const {
820   return braille_display_connected_;
821 }
822 
CheckBrailleState()823 void AccessibilityManager::CheckBrailleState() {
824   BrailleController* braille_controller = GetBrailleController();
825   if (!scoped_braille_observer_.IsObserving(braille_controller))
826     scoped_braille_observer_.Add(braille_controller);
827   BrowserThread::PostTaskAndReplyWithResult(
828       BrowserThread::IO,
829       FROM_HERE,
830       base::Bind(&BrailleController::GetDisplayState,
831                  base::Unretained(braille_controller)),
832       base::Bind(&AccessibilityManager::ReceiveBrailleDisplayState,
833                  weak_ptr_factory_.GetWeakPtr()));
834 }
835 
ReceiveBrailleDisplayState(scoped_ptr<extensions::api::braille_display_private::DisplayState> state)836 void AccessibilityManager::ReceiveBrailleDisplayState(
837     scoped_ptr<extensions::api::braille_display_private::DisplayState> state) {
838   OnBrailleDisplayStateChanged(*state);
839 }
840 
UpdateBrailleImeState()841 void AccessibilityManager::UpdateBrailleImeState() {
842   if (!profile_)
843     return;
844   PrefService* pref_service = profile_->GetPrefs();
845   std::vector<std::string> preload_engines;
846   base::SplitString(pref_service->GetString(prefs::kLanguagePreloadEngines),
847                     ',',
848                     &preload_engines);
849   std::vector<std::string>::iterator it =
850       std::find(preload_engines.begin(),
851                 preload_engines.end(),
852                 extension_misc::kBrailleImeEngineId);
853   bool is_enabled = (it != preload_engines.end());
854   bool should_be_enabled =
855       (spoken_feedback_enabled_ && braille_display_connected_);
856   if (is_enabled == should_be_enabled)
857     return;
858   if (should_be_enabled)
859     preload_engines.push_back(extension_misc::kBrailleImeEngineId);
860   else
861     preload_engines.erase(it);
862   pref_service->SetString(prefs::kLanguagePreloadEngines,
863                           JoinString(preload_engines, ','));
864   braille_ime_current_ = false;
865 }
866 
867 // Overridden from InputMethodManager::Observer.
InputMethodChanged(input_method::InputMethodManager * manager,bool show_message)868 void AccessibilityManager::InputMethodChanged(
869     input_method::InputMethodManager* manager,
870     bool show_message) {
871 #if defined(USE_ASH) && !defined(USE_ATHENA)
872   // Sticky keys is implemented only in ash.
873   // TODO(dpolukhin): support Athena, crbug.com/408733.
874   ash::Shell::GetInstance()->sticky_keys_controller()->SetModifiersEnabled(
875       manager->IsISOLevel5ShiftUsedByCurrentInputMethod(),
876       manager->IsAltGrUsedByCurrentInputMethod());
877 #endif
878   const chromeos::input_method::InputMethodDescriptor descriptor =
879       manager->GetActiveIMEState()->GetCurrentInputMethod();
880   braille_ime_current_ =
881       (descriptor.id() == extension_misc::kBrailleImeEngineId);
882 }
883 
SetProfile(Profile * profile)884 void AccessibilityManager::SetProfile(Profile* profile) {
885   pref_change_registrar_.reset();
886   local_state_pref_change_registrar_.reset();
887 
888   if (profile) {
889     // TODO(yoshiki): Move following code to PrefHandler.
890     pref_change_registrar_.reset(new PrefChangeRegistrar);
891     pref_change_registrar_->Init(profile->GetPrefs());
892     pref_change_registrar_->Add(
893         prefs::kAccessibilityLargeCursorEnabled,
894         base::Bind(&AccessibilityManager::UpdateLargeCursorFromPref,
895                    base::Unretained(this)));
896     pref_change_registrar_->Add(
897         prefs::kAccessibilityStickyKeysEnabled,
898         base::Bind(&AccessibilityManager::UpdateStickyKeysFromPref,
899                    base::Unretained(this)));
900     pref_change_registrar_->Add(
901         prefs::kAccessibilitySpokenFeedbackEnabled,
902         base::Bind(&AccessibilityManager::UpdateSpokenFeedbackFromPref,
903                    base::Unretained(this)));
904     pref_change_registrar_->Add(
905         prefs::kAccessibilityHighContrastEnabled,
906         base::Bind(&AccessibilityManager::UpdateHighContrastFromPref,
907                    base::Unretained(this)));
908     pref_change_registrar_->Add(
909         prefs::kAccessibilityAutoclickEnabled,
910         base::Bind(&AccessibilityManager::UpdateAutoclickFromPref,
911                    base::Unretained(this)));
912     pref_change_registrar_->Add(
913         prefs::kAccessibilityAutoclickDelayMs,
914         base::Bind(&AccessibilityManager::UpdateAutoclickDelayFromPref,
915                    base::Unretained(this)));
916     pref_change_registrar_->Add(
917         prefs::kAccessibilityVirtualKeyboardEnabled,
918         base::Bind(&AccessibilityManager::UpdateVirtualKeyboardFromPref,
919                    base::Unretained(this)));
920 
921     local_state_pref_change_registrar_.reset(new PrefChangeRegistrar);
922     local_state_pref_change_registrar_->Init(g_browser_process->local_state());
923     local_state_pref_change_registrar_->Add(
924         prefs::kApplicationLocale,
925         base::Bind(&AccessibilityManager::OnLocaleChanged,
926                    base::Unretained(this)));
927 
928     content::BrowserAccessibilityState::GetInstance()->AddHistogramCallback(
929         base::Bind(
930             &AccessibilityManager::UpdateChromeOSAccessibilityHistograms,
931             base::Unretained(this)));
932   }
933 
934   large_cursor_pref_handler_.HandleProfileChanged(profile_, profile);
935   spoken_feedback_pref_handler_.HandleProfileChanged(profile_, profile);
936   high_contrast_pref_handler_.HandleProfileChanged(profile_, profile);
937   autoclick_pref_handler_.HandleProfileChanged(profile_, profile);
938   autoclick_delay_pref_handler_.HandleProfileChanged(profile_, profile);
939   virtual_keyboard_pref_handler_.HandleProfileChanged(profile_, profile);
940 
941   bool had_profile = (profile_ != NULL);
942   profile_ = profile;
943 
944   if (!had_profile && profile)
945     CheckBrailleState();
946   else
947     UpdateBrailleImeState();
948   UpdateLargeCursorFromPref();
949   UpdateStickyKeysFromPref();
950   UpdateSpokenFeedbackFromPref();
951   UpdateHighContrastFromPref();
952   UpdateAutoclickFromPref();
953   UpdateAutoclickDelayFromPref();
954   UpdateVirtualKeyboardFromPref();
955 }
956 
ActiveUserChanged(const std::string & user_id)957 void AccessibilityManager::ActiveUserChanged(const std::string& user_id) {
958   SetProfile(ProfileManager::GetActiveUserProfile());
959 }
960 
SetProfileForTest(Profile * profile)961 void AccessibilityManager::SetProfileForTest(Profile* profile) {
962   SetProfile(profile);
963 }
964 
SetBrailleControllerForTest(BrailleController * controller)965 void AccessibilityManager::SetBrailleControllerForTest(
966     BrailleController* controller) {
967   g_braille_controller_for_test = controller;
968 }
969 
EnableSystemSounds(bool system_sounds_enabled)970 void AccessibilityManager::EnableSystemSounds(bool system_sounds_enabled) {
971   system_sounds_enabled_ = system_sounds_enabled;
972 }
973 
PlayShutdownSound()974 base::TimeDelta AccessibilityManager::PlayShutdownSound() {
975   if (!system_sounds_enabled_)
976     return base::TimeDelta();
977   system_sounds_enabled_ = false;
978 #if !defined(USE_ATHENA)
979   if (!ash::PlaySystemSoundIfSpokenFeedback(SOUND_SHUTDOWN))
980     return base::TimeDelta();
981 #endif
982   return media::SoundsManager::Get()->GetDuration(SOUND_SHUTDOWN);
983 }
984 
InjectChromeVox(RenderViewHost * render_view_host)985 void AccessibilityManager::InjectChromeVox(RenderViewHost* render_view_host) {
986   LoadChromeVoxExtension(profile_, render_view_host, base::Closure());
987 }
988 
989 scoped_ptr<AccessibilityStatusSubscription>
RegisterCallback(const AccessibilityStatusCallback & cb)990     AccessibilityManager::RegisterCallback(
991         const AccessibilityStatusCallback& cb) {
992   return callback_list_.Add(cb);
993 }
994 
NotifyAccessibilityStatusChanged(AccessibilityStatusEventDetails & details)995 void AccessibilityManager::NotifyAccessibilityStatusChanged(
996     AccessibilityStatusEventDetails& details) {
997   callback_list_.Notify(details);
998 }
999 
UpdateChromeOSAccessibilityHistograms()1000 void AccessibilityManager::UpdateChromeOSAccessibilityHistograms() {
1001   UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosSpokenFeedback",
1002                         IsSpokenFeedbackEnabled());
1003   UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosHighContrast",
1004                         IsHighContrastEnabled());
1005   UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosVirtualKeyboard",
1006                         IsVirtualKeyboardEnabled());
1007   UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosStickyKeys", IsStickyKeysEnabled());
1008   if (MagnificationManager::Get()) {
1009     uint32 type = MagnificationManager::Get()->IsMagnifierEnabled() ?
1010                       MagnificationManager::Get()->GetMagnifierType() : 0;
1011     // '0' means magnifier is disabled.
1012     UMA_HISTOGRAM_ENUMERATION("Accessibility.CrosScreenMagnifier",
1013                               type,
1014                               ash::kMaxMagnifierType + 1);
1015   }
1016   if (profile_) {
1017     const PrefService* const prefs = profile_->GetPrefs();
1018     UMA_HISTOGRAM_BOOLEAN(
1019         "Accessibility.CrosLargeCursor",
1020         prefs->GetBoolean(prefs::kAccessibilityLargeCursorEnabled));
1021     UMA_HISTOGRAM_BOOLEAN(
1022         "Accessibility.CrosAlwaysShowA11yMenu",
1023         prefs->GetBoolean(prefs::kShouldAlwaysShowAccessibilityMenu));
1024 
1025     bool autoclick_enabled =
1026         prefs->GetBoolean(prefs::kAccessibilityAutoclickEnabled);
1027     UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosAutoclick", autoclick_enabled);
1028     if (autoclick_enabled) {
1029       // We only want to log the autoclick delay if the user has actually
1030       // enabled autoclick.
1031       UMA_HISTOGRAM_CUSTOM_TIMES(
1032           "Accessibility.CrosAutoclickDelay",
1033           base::TimeDelta::FromMilliseconds(
1034               prefs->GetInteger(prefs::kAccessibilityAutoclickDelayMs)),
1035           base::TimeDelta::FromMilliseconds(1),
1036           base::TimeDelta::FromMilliseconds(3000),
1037           50);
1038     }
1039   }
1040 }
1041 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)1042 void AccessibilityManager::Observe(
1043     int type,
1044     const content::NotificationSource& source,
1045     const content::NotificationDetails& details) {
1046   switch (type) {
1047     case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
1048       // Update |profile_| when entering the login screen.
1049       Profile* profile = ProfileManager::GetActiveUserProfile();
1050       if (ProfileHelper::IsSigninProfile(profile))
1051         SetProfile(profile);
1052       break;
1053     }
1054     case chrome::NOTIFICATION_SESSION_STARTED:
1055       // Update |profile_| when entering a session.
1056       SetProfile(ProfileManager::GetActiveUserProfile());
1057 
1058       // Ensure ChromeVox makes announcements at the start of new sessions.
1059       should_speak_chrome_vox_announcements_on_user_screen_ = true;
1060 
1061       // Add a session state observer to be able to monitor session changes.
1062       if (!session_state_observer_.get() && ash::Shell::HasInstance())
1063         session_state_observer_.reset(
1064             new ash::ScopedSessionStateObserver(this));
1065       break;
1066     case chrome::NOTIFICATION_PROFILE_DESTROYED: {
1067       // Update |profile_| when exiting a session or shutting down.
1068       Profile* profile = content::Source<Profile>(source).ptr();
1069       if (profile_ == profile)
1070         SetProfile(NULL);
1071       break;
1072     }
1073     case chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED: {
1074       bool is_screen_locked = *content::Details<bool>(details).ptr();
1075       if (spoken_feedback_enabled_) {
1076         if (is_screen_locked)
1077           LoadChromeVoxToLockScreen(base::Closure());
1078         // If spoken feedback was enabled, make sure it is also enabled on
1079         // the user screen.
1080         // The status tray gets verbalized by user screen ChromeVox, so we need
1081         // to load it on the user screen even if the screen is locked.
1082         LoadChromeVoxToUserScreen(base::Closure());
1083       }
1084       break;
1085     }
1086   }
1087 }
1088 
OnBrailleDisplayStateChanged(const DisplayState & display_state)1089 void AccessibilityManager::OnBrailleDisplayStateChanged(
1090     const DisplayState& display_state) {
1091   braille_display_connected_ = display_state.available;
1092   if (braille_display_connected_) {
1093     EnableSpokenFeedback(true, ash::A11Y_NOTIFICATION_SHOW);
1094   }
1095   UpdateBrailleImeState();
1096 
1097   AccessibilityStatusEventDetails details(
1098       ACCESSIBILITY_BRAILLE_DISPLAY_CONNECTION_STATE_CHANGED,
1099       braille_display_connected_,
1100       ash::A11Y_NOTIFICATION_SHOW);
1101   NotifyAccessibilityStatusChanged(details);
1102 }
1103 
OnBrailleKeyEvent(const KeyEvent & event)1104 void AccessibilityManager::OnBrailleKeyEvent(const KeyEvent& event) {
1105   // Ensure the braille IME is active on braille keyboard (dots) input.
1106   if ((event.command ==
1107        extensions::api::braille_display_private::KEY_COMMAND_DOTS) &&
1108       !braille_ime_current_) {
1109     input_method::InputMethodManager::Get()
1110         ->GetActiveIMEState()
1111         ->ChangeInputMethod(extension_misc::kBrailleImeEngineId,
1112                             false /* show_message */);
1113   }
1114 }
1115 
PostLoadChromeVox(Profile * profile)1116 void AccessibilityManager::PostLoadChromeVox(Profile* profile) {
1117   // Do any setup work needed immediately after ChromeVox actually loads.
1118   if (system_sounds_enabled_)
1119     ash::PlaySystemSoundAlways(SOUND_SPOKEN_FEEDBACK_ENABLED);
1120 
1121   ExtensionAccessibilityEventRouter::GetInstance()->
1122       OnChromeVoxLoadStateChanged(profile_,
1123           IsSpokenFeedbackEnabled(),
1124           chrome_vox_loaded_on_lock_screen_ ||
1125               should_speak_chrome_vox_announcements_on_user_screen_);
1126 
1127   should_speak_chrome_vox_announcements_on_user_screen_ =
1128       chrome_vox_loaded_on_lock_screen_;
1129 }
1130 
PostUnloadChromeVox(Profile * profile)1131 void AccessibilityManager::PostUnloadChromeVox(Profile* profile) {
1132   // Do any teardown work needed immediately after ChromeVox actually unloads.
1133   if (system_sounds_enabled_)
1134     ash::PlaySystemSoundAlways(SOUND_SPOKEN_FEEDBACK_DISABLED);
1135   // Clear the accessibility focus ring.
1136   AccessibilityFocusRingController::GetInstance()->SetFocusRing(
1137       std::vector<gfx::Rect>());
1138 }
1139 
1140 }  // namespace chromeos
1141