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