• 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/autoclick/autoclick_controller.h"
8 #include "ash/high_contrast/high_contrast_controller.h"
9 #include "ash/metrics/user_metrics_recorder.h"
10 #include "ash/session_state_delegate.h"
11 #include "ash/shell.h"
12 #include "ash/system/tray/system_tray_notifier.h"
13 #include "ash/wm/event_rewriter_event_filter.h"
14 #include "ash/wm/sticky_keys.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/memory/singleton.h"
17 #include "base/metrics/histogram.h"
18 #include "base/path_service.h"
19 #include "base/prefs/pref_member.h"
20 #include "base/prefs/pref_service.h"
21 #include "base/time/time.h"
22 #include "base/values.h"
23 #include "chrome/browser/accessibility/accessibility_extension_api.h"
24 #include "chrome/browser/browser_process.h"
25 #include "chrome/browser/chrome_notification_types.h"
26 #include "chrome/browser/chromeos/accessibility/magnification_manager.h"
27 #include "chrome/browser/chromeos/login/login_display_host.h"
28 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
29 #include "chrome/browser/chromeos/login/screen_locker.h"
30 #include "chrome/browser/chromeos/login/user_manager.h"
31 #include "chrome/browser/chromeos/login/webui_login_view.h"
32 #include "chrome/browser/chromeos/profiles/profile_helper.h"
33 #include "chrome/browser/extensions/component_loader.h"
34 #include "chrome/browser/extensions/extension_service.h"
35 #include "chrome/browser/extensions/extension_system.h"
36 #include "chrome/browser/profiles/profile.h"
37 #include "chrome/browser/profiles/profile_manager.h"
38 #include "chrome/common/chrome_paths.h"
39 #include "chrome/common/extensions/api/experimental_accessibility.h"
40 #include "chrome/common/extensions/extension_messages.h"
41 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
42 #include "chrome/common/pref_names.h"
43 #include "chromeos/audio/chromeos_sounds.h"
44 #include "chromeos/login/login_state.h"
45 #include "content/public/browser/browser_accessibility_state.h"
46 #include "content/public/browser/browser_thread.h"
47 #include "content/public/browser/notification_details.h"
48 #include "content/public/browser/notification_service.h"
49 #include "content/public/browser/notification_source.h"
50 #include "content/public/browser/render_process_host.h"
51 #include "content/public/browser/render_view_host.h"
52 #include "content/public/browser/web_contents.h"
53 #include "content/public/browser/web_ui.h"
54 #include "extensions/browser/file_reader.h"
55 #include "extensions/common/extension.h"
56 #include "extensions/common/extension_resource.h"
57 #include "grit/browser_resources.h"
58 #include "grit/generated_resources.h"
59 #include "media/audio/sounds/sounds_manager.h"
60 #include "ui/base/l10n/l10n_util.h"
61 #include "ui/base/resource/resource_bundle.h"
62 
63 using content::BrowserThread;
64 using content::RenderViewHost;
65 using extensions::api::braille_display_private::BrailleController;
66 using extensions::api::braille_display_private::DisplayState;
67 
68 namespace chromeos {
69 
70 namespace {
71 
72 static chromeos::AccessibilityManager* g_accessibility_manager = NULL;
73 
74 static BrailleController* g_braille_controller_for_test = NULL;
75 
GetBrailleController()76 BrailleController* GetBrailleController() {
77   return g_braille_controller_for_test
78       ? g_braille_controller_for_test
79       : BrailleController::GetInstance();
80 }
81 
GetChromeVoxPath()82 base::FilePath GetChromeVoxPath() {
83   base::FilePath path;
84   if (!PathService::Get(chrome::DIR_RESOURCES, &path))
85     NOTREACHED();
86   path = path.Append(extension_misc::kChromeVoxExtensionPath);
87   return path;
88 }
89 
90 // Helper class that directly loads an extension's content scripts into
91 // all of the frames corresponding to a given RenderViewHost.
92 class ContentScriptLoader {
93  public:
94   // Initialize the ContentScriptLoader with the ID of the extension
95   // and the RenderViewHost where the scripts should be loaded.
ContentScriptLoader(const std::string & extension_id,int render_process_id,int render_view_id)96   ContentScriptLoader(const std::string& extension_id,
97                       int render_process_id,
98                       int render_view_id)
99       : extension_id_(extension_id),
100         render_process_id_(render_process_id),
101         render_view_id_(render_view_id) {}
102 
103   // Call this once with the ExtensionResource corresponding to each
104   // content script to be loaded.
AppendScript(extensions::ExtensionResource resource)105   void AppendScript(extensions::ExtensionResource resource) {
106     resources_.push(resource);
107   }
108 
109   // Finally, call this method once to fetch all of the resources and
110   // load them. This method will delete this object when done.
Run()111   void Run() {
112     if (resources_.empty()) {
113       delete this;
114       return;
115     }
116 
117     extensions::ExtensionResource resource = resources_.front();
118     resources_.pop();
119     scoped_refptr<FileReader> reader(new FileReader(resource, base::Bind(
120         &ContentScriptLoader::OnFileLoaded, base::Unretained(this))));
121     reader->Start();
122   }
123 
124  private:
OnFileLoaded(bool success,const std::string & data)125   void OnFileLoaded(bool success, const std::string& data) {
126     if (success) {
127       ExtensionMsg_ExecuteCode_Params params;
128       params.request_id = 0;
129       params.extension_id = extension_id_;
130       params.is_javascript = true;
131       params.code = data;
132       params.run_at = extensions::UserScript::DOCUMENT_IDLE;
133       params.all_frames = true;
134       params.in_main_world = false;
135 
136       RenderViewHost* render_view_host =
137           RenderViewHost::FromID(render_process_id_, render_view_id_);
138       if (render_view_host) {
139         render_view_host->Send(new ExtensionMsg_ExecuteCode(
140             render_view_host->GetRoutingID(), params));
141       }
142     }
143     Run();
144   }
145 
146   std::string extension_id_;
147   int render_process_id_;
148   int render_view_id_;
149   std::queue<extensions::ExtensionResource> resources_;
150 };
151 
LoadChromeVoxExtension(Profile * profile,content::WebUI * login_web_ui)152 void LoadChromeVoxExtension(Profile* profile, content::WebUI* login_web_ui) {
153   ExtensionService* extension_service =
154       extensions::ExtensionSystem::Get(profile)->extension_service();
155   std::string extension_id =
156       extension_service->component_loader()->AddChromeVoxExtension();
157   if (login_web_ui) {
158     ExtensionService* extension_service =
159         extensions::ExtensionSystem::Get(profile)->extension_service();
160     const extensions::Extension* extension =
161         extension_service->extensions()->GetByID(extension_id);
162 
163     RenderViewHost* render_view_host =
164         login_web_ui->GetWebContents()->GetRenderViewHost();
165     // Set a flag to tell ChromeVox that it's just been enabled,
166     // so that it won't interrupt our speech feedback enabled message.
167     ExtensionMsg_ExecuteCode_Params params;
168     params.request_id = 0;
169     params.extension_id = extension->id();
170     params.is_javascript = true;
171     params.code = "window.INJECTED_AFTER_LOAD = true;";
172     params.run_at = extensions::UserScript::DOCUMENT_IDLE;
173     params.all_frames = true;
174     params.in_main_world = false;
175     render_view_host->Send(new ExtensionMsg_ExecuteCode(
176         render_view_host->GetRoutingID(), params));
177 
178     // Inject ChromeVox' content scripts.
179     ContentScriptLoader* loader = new ContentScriptLoader(
180         extension->id(), render_view_host->GetProcess()->GetID(),
181         render_view_host->GetRoutingID());
182 
183     const extensions::UserScriptList& content_scripts =
184         extensions::ContentScriptsInfo::GetContentScripts(extension);
185     for (size_t i = 0; i < content_scripts.size(); i++) {
186       const extensions::UserScript& script = content_scripts[i];
187       for (size_t j = 0; j < script.js_scripts().size(); ++j) {
188         const extensions::UserScript::File &file = script.js_scripts()[j];
189         extensions::ExtensionResource resource = extension->GetResource(
190             file.relative_path());
191         loader->AppendScript(resource);
192       }
193     }
194     loader->Run();  // It cleans itself up when done.
195   }
196 }
197 
UnloadChromeVoxExtension(Profile * profile)198 void UnloadChromeVoxExtension(Profile* profile) {
199   base::FilePath path = GetChromeVoxPath();
200   ExtensionService* extension_service =
201       extensions::ExtensionSystem::Get(profile)->extension_service();
202   extension_service->component_loader()->Remove(path);
203 }
204 
205 }  // namespace
206 
207 ///////////////////////////////////////////////////////////////////////////////
208 // AccessibilityStatusEventDetails
209 
AccessibilityStatusEventDetails(bool enabled,ash::AccessibilityNotificationVisibility notify)210 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
211     bool enabled,
212     ash::AccessibilityNotificationVisibility notify)
213   : enabled(enabled),
214     magnifier_type(ash::kDefaultMagnifierType),
215     notify(notify) {}
216 
AccessibilityStatusEventDetails(bool enabled,ash::MagnifierType magnifier_type,ash::AccessibilityNotificationVisibility notify)217 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
218     bool enabled,
219     ash::MagnifierType magnifier_type,
220     ash::AccessibilityNotificationVisibility notify)
221   : enabled(enabled),
222     magnifier_type(magnifier_type),
223     notify(notify) {}
224 
225 ///////////////////////////////////////////////////////////////////////////////
226 //
227 // AccessibilityManager::PrefHandler
228 
PrefHandler(const char * pref_path)229 AccessibilityManager::PrefHandler::PrefHandler(const char* pref_path)
230     : pref_path_(pref_path) {}
231 
~PrefHandler()232 AccessibilityManager::PrefHandler::~PrefHandler() {}
233 
HandleProfileChanged(Profile * previous_profile,Profile * current_profile)234 void AccessibilityManager::PrefHandler::HandleProfileChanged(
235     Profile* previous_profile, Profile* current_profile) {
236   // Returns if the current profile is null.
237   if (!current_profile)
238     return;
239 
240   // If the user set a pref value on the login screen and is now starting a
241   // session with a new profile, copy the pref value to the profile.
242   if ((previous_profile &&
243        ProfileHelper::IsSigninProfile(previous_profile) &&
244        current_profile->IsNewProfile() &&
245        !ProfileHelper::IsSigninProfile(current_profile)) ||
246       // Special case for Guest mode:
247       // Guest mode launches a guest-mode browser process before session starts,
248       // so the previous profile is null.
249       (!previous_profile &&
250        current_profile->IsGuestSession())) {
251     // Returns if the pref has not been set by the user.
252     const PrefService::Preference* pref = ProfileHelper::GetSigninProfile()->
253         GetPrefs()->FindPreference(pref_path_);
254     if (!pref || !pref->IsUserControlled())
255       return;
256 
257     // Copy the pref value from the signin screen.
258     const base::Value* value_on_login = pref->GetValue();
259     PrefService* user_prefs = current_profile->GetPrefs();
260     user_prefs->Set(pref_path_, *value_on_login);
261   }
262 }
263 
264 ///////////////////////////////////////////////////////////////////////////////
265 //
266 // AccessibilityManager
267 
268 // static
Initialize()269 void AccessibilityManager::Initialize() {
270   CHECK(g_accessibility_manager == NULL);
271   g_accessibility_manager = new AccessibilityManager();
272 }
273 
274 // static
Shutdown()275 void AccessibilityManager::Shutdown() {
276   CHECK(g_accessibility_manager);
277   delete g_accessibility_manager;
278   g_accessibility_manager = NULL;
279 }
280 
281 // static
Get()282 AccessibilityManager* AccessibilityManager::Get() {
283   return g_accessibility_manager;
284 }
285 
AccessibilityManager()286 AccessibilityManager::AccessibilityManager()
287     : profile_(NULL),
288       chrome_vox_loaded_on_lock_screen_(false),
289       chrome_vox_loaded_on_user_screen_(false),
290       large_cursor_pref_handler_(prefs::kLargeCursorEnabled),
291       spoken_feedback_pref_handler_(prefs::kSpokenFeedbackEnabled),
292       high_contrast_pref_handler_(prefs::kHighContrastEnabled),
293       autoclick_pref_handler_(prefs::kAutoclickEnabled),
294       autoclick_delay_pref_handler_(prefs::kAutoclickDelayMs),
295       large_cursor_enabled_(false),
296       sticky_keys_enabled_(false),
297       spoken_feedback_enabled_(false),
298       high_contrast_enabled_(false),
299       autoclick_enabled_(false),
300       autoclick_delay_ms_(ash::AutoclickController::kDefaultAutoclickDelayMs),
301       spoken_feedback_notification_(ash::A11Y_NOTIFICATION_NONE),
302       weak_ptr_factory_(this),
303       should_speak_chrome_vox_announcements_on_user_screen_(true),
304       system_sounds_enabled_(false) {
305   notification_registrar_.Add(this,
306                               chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
307                               content::NotificationService::AllSources());
308   notification_registrar_.Add(this,
309                               chrome::NOTIFICATION_SESSION_STARTED,
310                               content::NotificationService::AllSources());
311   notification_registrar_.Add(this,
312                               chrome::NOTIFICATION_PROFILE_DESTROYED,
313                               content::NotificationService::AllSources());
314   notification_registrar_.Add(this,
315                               chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED,
316                               content::NotificationService::AllSources());
317 
318   GetBrailleController()->AddObserver(this);
319 
320   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
321   media::SoundsManager* manager = media::SoundsManager::Get();
322   manager->Initialize(SOUND_SHUTDOWN,
323                       bundle.GetRawDataResource(IDR_SOUND_SHUTDOWN_WAV));
324   manager->Initialize(
325       SOUND_SPOKEN_FEEDBACK_ENABLED,
326       bundle.GetRawDataResource(IDR_SOUND_SPOKEN_FEEDBACK_ENABLED_WAV));
327   manager->Initialize(
328       SOUND_SPOKEN_FEEDBACK_DISABLED,
329       bundle.GetRawDataResource(IDR_SOUND_SPOKEN_FEEDBACK_DISABLED_WAV));
330 }
331 
~AccessibilityManager()332 AccessibilityManager::~AccessibilityManager() {
333   CHECK(this == g_accessibility_manager);
334 }
335 
ShouldShowAccessibilityMenu()336 bool AccessibilityManager::ShouldShowAccessibilityMenu() {
337   // If any of the loaded profiles has an accessibility feature turned on - or
338   // enforced to always show the menu - we return true to show the menu.
339   std::vector<Profile*> profiles =
340       g_browser_process->profile_manager()->GetLoadedProfiles();
341   for (std::vector<Profile*>::iterator it = profiles.begin();
342        it != profiles.end();
343        ++it) {
344     PrefService* pref_service = (*it)->GetPrefs();
345     if (pref_service->GetBoolean(prefs::kStickyKeysEnabled) ||
346         pref_service->GetBoolean(prefs::kLargeCursorEnabled) ||
347         pref_service->GetBoolean(prefs::kSpokenFeedbackEnabled) ||
348         pref_service->GetBoolean(prefs::kHighContrastEnabled) ||
349         pref_service->GetBoolean(prefs::kAutoclickEnabled) ||
350         pref_service->GetBoolean(prefs::kShouldAlwaysShowAccessibilityMenu) ||
351         pref_service->GetBoolean(prefs::kScreenMagnifierEnabled))
352       return true;
353   }
354   return false;
355 }
356 
EnableLargeCursor(bool enabled)357 void AccessibilityManager::EnableLargeCursor(bool enabled) {
358   if (!profile_)
359     return;
360 
361   PrefService* pref_service = profile_->GetPrefs();
362   pref_service->SetBoolean(prefs::kLargeCursorEnabled, enabled);
363   pref_service->CommitPendingWrite();
364 }
365 
UpdateLargeCursorFromPref()366 void AccessibilityManager::UpdateLargeCursorFromPref() {
367   if (!profile_)
368     return;
369 
370   const bool enabled =
371       profile_->GetPrefs()->GetBoolean(prefs::kLargeCursorEnabled);
372 
373   if (large_cursor_enabled_ == enabled)
374     return;
375 
376   large_cursor_enabled_ = enabled;
377 
378   AccessibilityStatusEventDetails details(enabled, ash::A11Y_NOTIFICATION_NONE);
379   content::NotificationService::current()->Notify(
380       chrome::NOTIFICATION_CROS_ACCESSIBILITY_TOGGLE_LARGE_CURSOR,
381       content::NotificationService::AllSources(),
382       content::Details<AccessibilityStatusEventDetails>(&details));
383 
384 #if defined(USE_ASH)
385   // Large cursor is implemented only in ash.
386   ash::Shell::GetInstance()->cursor_manager()->SetCursorSet(
387       enabled ? ui::CURSOR_SET_LARGE : ui::CURSOR_SET_NORMAL);
388 #endif
389 }
390 
IsIncognitoAllowed()391 bool AccessibilityManager::IsIncognitoAllowed() {
392   UserManager* user_manager = UserManager::Get();
393   // Supervised users can't create incognito-mode windows.
394   return !(user_manager->IsLoggedInAsLocallyManagedUser());
395 }
396 
IsLargeCursorEnabled()397 bool AccessibilityManager::IsLargeCursorEnabled() {
398   return large_cursor_enabled_;
399 }
400 
EnableStickyKeys(bool enabled)401 void AccessibilityManager::EnableStickyKeys(bool enabled) {
402   if (!profile_)
403     return;
404   PrefService* pref_service = profile_->GetPrefs();
405   pref_service->SetBoolean(prefs::kStickyKeysEnabled, enabled);
406   pref_service->CommitPendingWrite();
407 }
408 
IsStickyKeysEnabled()409 bool AccessibilityManager::IsStickyKeysEnabled() {
410   return sticky_keys_enabled_;
411 }
412 
UpdateStickyKeysFromPref()413 void AccessibilityManager::UpdateStickyKeysFromPref() {
414   if (!profile_)
415     return;
416 
417   const bool enabled =
418       profile_->GetPrefs()->GetBoolean(prefs::kStickyKeysEnabled);
419 
420   if (sticky_keys_enabled_ == enabled)
421     return;
422 
423   sticky_keys_enabled_ = enabled;
424 #if defined(USE_ASH)
425   // Sticky keys is implemented only in ash.
426   ash::Shell::GetInstance()->sticky_keys()->Enable(enabled);
427 #endif
428 }
429 
EnableSpokenFeedback(bool enabled,ash::AccessibilityNotificationVisibility notify)430 void AccessibilityManager::EnableSpokenFeedback(
431     bool enabled,
432     ash::AccessibilityNotificationVisibility notify) {
433   if (!profile_)
434     return;
435 
436   ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(
437       enabled ? ash::UMA_STATUS_AREA_ENABLE_SPOKEN_FEEDBACK
438               : ash::UMA_STATUS_AREA_DISABLE_SPOKEN_FEEDBACK);
439 
440   spoken_feedback_notification_ = notify;
441 
442   PrefService* pref_service = profile_->GetPrefs();
443   pref_service->SetBoolean(
444       prefs::kSpokenFeedbackEnabled, enabled);
445   pref_service->CommitPendingWrite();
446 
447   spoken_feedback_notification_ = ash::A11Y_NOTIFICATION_NONE;
448 }
449 
UpdateSpokenFeedbackFromPref()450 void AccessibilityManager::UpdateSpokenFeedbackFromPref() {
451   if (!profile_)
452     return;
453 
454   const bool enabled =
455       profile_->GetPrefs()->GetBoolean(prefs::kSpokenFeedbackEnabled);
456 
457   if (spoken_feedback_enabled_ == enabled)
458     return;
459 
460   spoken_feedback_enabled_ = enabled;
461 
462   ExtensionAccessibilityEventRouter::GetInstance()->
463       SetAccessibilityEnabled(enabled);
464 
465   AccessibilityStatusEventDetails details(enabled,
466                                           spoken_feedback_notification_);
467   content::NotificationService::current()->Notify(
468       chrome::NOTIFICATION_CROS_ACCESSIBILITY_TOGGLE_SPOKEN_FEEDBACK,
469       content::NotificationService::AllSources(),
470       content::Details<AccessibilityStatusEventDetails>(&details));
471 
472   if (enabled) {
473     LoadChromeVox();
474   } else {
475     UnloadChromeVox();
476   }
477 }
478 
LoadChromeVox()479 void AccessibilityManager::LoadChromeVox() {
480   ScreenLocker* screen_locker = ScreenLocker::default_screen_locker();
481   if (screen_locker && screen_locker->locked()) {
482     // If on the lock screen, loads ChromeVox only to the lock screen as for
483     // now. On unlock, it will be loaded to the user screen.
484     // (see. AccessibilityManager::Observe())
485     LoadChromeVoxToLockScreen();
486   } else {
487     LoadChromeVoxToUserScreen();
488   }
489   PostLoadChromeVox(profile_);
490 }
491 
LoadChromeVoxToUserScreen()492 void AccessibilityManager::LoadChromeVoxToUserScreen() {
493   if (chrome_vox_loaded_on_user_screen_)
494     return;
495 
496   // Determine whether an OOBE screen is currently being shown. If so,
497   // ChromeVox will be injected directly into that screen.
498   content::WebUI* login_web_ui = NULL;
499 
500   if (ProfileHelper::IsSigninProfile(profile_)) {
501     LoginDisplayHost* login_display_host = LoginDisplayHostImpl::default_host();
502     if (login_display_host) {
503       WebUILoginView* web_ui_login_view =
504           login_display_host->GetWebUILoginView();
505       if (web_ui_login_view)
506         login_web_ui = web_ui_login_view->GetWebUI();
507     }
508   }
509 
510   LoadChromeVoxExtension(profile_, login_web_ui);
511   chrome_vox_loaded_on_user_screen_ = true;
512 }
513 
LoadChromeVoxToLockScreen()514 void AccessibilityManager::LoadChromeVoxToLockScreen() {
515   if (chrome_vox_loaded_on_lock_screen_)
516     return;
517 
518   ScreenLocker* screen_locker = ScreenLocker::default_screen_locker();
519   if (screen_locker && screen_locker->locked()) {
520     content::WebUI* lock_web_ui = screen_locker->GetAssociatedWebUI();
521     if (lock_web_ui) {
522       Profile* profile = Profile::FromWebUI(lock_web_ui);
523       LoadChromeVoxExtension(profile, lock_web_ui);
524       chrome_vox_loaded_on_lock_screen_ = true;
525     }
526   }
527 }
528 
UnloadChromeVox()529 void AccessibilityManager::UnloadChromeVox() {
530   if (chrome_vox_loaded_on_lock_screen_)
531     UnloadChromeVoxFromLockScreen();
532 
533   if (chrome_vox_loaded_on_user_screen_) {
534     UnloadChromeVoxExtension(profile_);
535     chrome_vox_loaded_on_user_screen_ = false;
536   }
537 
538   PostUnloadChromeVox(profile_);
539 }
540 
UnloadChromeVoxFromLockScreen()541 void AccessibilityManager::UnloadChromeVoxFromLockScreen() {
542   // Lock screen uses the signin progile.
543   Profile* signin_profile = ProfileHelper::GetSigninProfile();
544   UnloadChromeVoxExtension(signin_profile);
545   chrome_vox_loaded_on_lock_screen_ = false;
546 }
547 
IsSpokenFeedbackEnabled()548 bool AccessibilityManager::IsSpokenFeedbackEnabled() {
549   return spoken_feedback_enabled_;
550 }
551 
ToggleSpokenFeedback(ash::AccessibilityNotificationVisibility notify)552 void AccessibilityManager::ToggleSpokenFeedback(
553     ash::AccessibilityNotificationVisibility notify) {
554   EnableSpokenFeedback(!IsSpokenFeedbackEnabled(), notify);
555 }
556 
EnableHighContrast(bool enabled)557 void AccessibilityManager::EnableHighContrast(bool enabled) {
558   if (!profile_)
559     return;
560 
561   PrefService* pref_service = profile_->GetPrefs();
562   pref_service->SetBoolean(prefs::kHighContrastEnabled, enabled);
563   pref_service->CommitPendingWrite();
564 }
565 
UpdateHighContrastFromPref()566 void AccessibilityManager::UpdateHighContrastFromPref() {
567   if (!profile_)
568     return;
569 
570   const bool enabled =
571       profile_->GetPrefs()->GetBoolean(prefs::kHighContrastEnabled);
572 
573   if (high_contrast_enabled_ == enabled)
574     return;
575 
576   high_contrast_enabled_ = enabled;
577 
578   AccessibilityStatusEventDetails detail(enabled, ash::A11Y_NOTIFICATION_NONE);
579   content::NotificationService::current()->Notify(
580       chrome::NOTIFICATION_CROS_ACCESSIBILITY_TOGGLE_HIGH_CONTRAST_MODE,
581       content::NotificationService::AllSources(),
582       content::Details<AccessibilityStatusEventDetails>(&detail));
583 
584 #if defined(USE_ASH)
585   ash::Shell::GetInstance()->high_contrast_controller()->SetEnabled(enabled);
586 #endif
587 }
588 
LocalePrefChanged()589 void AccessibilityManager::LocalePrefChanged() {
590   if (!profile_)
591     return;
592 
593   if (!IsSpokenFeedbackEnabled())
594     return;
595 
596   // If the system locale changes and spoken feedback is enabled,
597   // reload ChromeVox so that it switches its internal translations
598   // to the new language.
599   EnableSpokenFeedback(false, ash::A11Y_NOTIFICATION_NONE);
600   EnableSpokenFeedback(true, ash::A11Y_NOTIFICATION_NONE);
601 }
602 
IsHighContrastEnabled()603 bool AccessibilityManager::IsHighContrastEnabled() {
604   return high_contrast_enabled_;
605 }
606 
EnableAutoclick(bool enabled)607 void AccessibilityManager::EnableAutoclick(bool enabled) {
608   if (!profile_)
609     return;
610 
611   PrefService* pref_service = profile_->GetPrefs();
612   pref_service->SetBoolean(prefs::kAutoclickEnabled, enabled);
613   pref_service->CommitPendingWrite();
614 }
615 
IsAutoclickEnabled()616 bool AccessibilityManager::IsAutoclickEnabled() {
617   return autoclick_enabled_;
618 }
619 
UpdateAutoclickFromPref()620 void AccessibilityManager::UpdateAutoclickFromPref() {
621   bool enabled =
622       profile_->GetPrefs()->GetBoolean(prefs::kAutoclickEnabled);
623 
624   if (autoclick_enabled_ == enabled)
625     return;
626   autoclick_enabled_ = enabled;
627 
628 #if defined(USE_ASH)
629   ash::Shell::GetInstance()->autoclick_controller()->SetEnabled(enabled);
630 #endif
631 }
632 
SetAutoclickDelay(int delay_ms)633 void AccessibilityManager::SetAutoclickDelay(int delay_ms) {
634   if (!profile_)
635     return;
636 
637   PrefService* pref_service = profile_->GetPrefs();
638   pref_service->SetInteger(prefs::kAutoclickDelayMs, delay_ms);
639   pref_service->CommitPendingWrite();
640 }
641 
GetAutoclickDelay() const642 int AccessibilityManager::GetAutoclickDelay() const {
643   return autoclick_delay_ms_;
644 }
645 
UpdateAutoclickDelayFromPref()646 void AccessibilityManager::UpdateAutoclickDelayFromPref() {
647   int autoclick_delay_ms =
648       profile_->GetPrefs()->GetInteger(prefs::kAutoclickDelayMs);
649 
650   if (autoclick_delay_ms == autoclick_delay_ms_)
651     return;
652   autoclick_delay_ms_ = autoclick_delay_ms;
653 
654 #if defined(USE_ASH)
655   ash::Shell::GetInstance()->autoclick_controller()->SetAutoclickDelay(
656       autoclick_delay_ms_);
657 #endif
658 }
659 
CheckBrailleState()660 void AccessibilityManager::CheckBrailleState() {
661   BrowserThread::PostTaskAndReplyWithResult(
662       BrowserThread::IO, FROM_HERE, base::Bind(
663           &BrailleController::GetDisplayState,
664           base::Unretained(GetBrailleController())),
665       base::Bind(&AccessibilityManager::ReceiveBrailleDisplayState,
666                  weak_ptr_factory_.GetWeakPtr()));
667 }
668 
ReceiveBrailleDisplayState(scoped_ptr<extensions::api::braille_display_private::DisplayState> state)669 void AccessibilityManager::ReceiveBrailleDisplayState(
670     scoped_ptr<extensions::api::braille_display_private::DisplayState> state) {
671   OnDisplayStateChanged(*state);
672 }
673 
674 
SetProfile(Profile * profile)675 void AccessibilityManager::SetProfile(Profile* profile) {
676   pref_change_registrar_.reset();
677   local_state_pref_change_registrar_.reset();
678 
679   if (profile) {
680     // TODO(yoshiki): Move following code to PrefHandler.
681     pref_change_registrar_.reset(new PrefChangeRegistrar);
682     pref_change_registrar_->Init(profile->GetPrefs());
683     pref_change_registrar_->Add(
684         prefs::kLargeCursorEnabled,
685         base::Bind(&AccessibilityManager::UpdateLargeCursorFromPref,
686                    base::Unretained(this)));
687     pref_change_registrar_->Add(
688         prefs::kStickyKeysEnabled,
689         base::Bind(&AccessibilityManager::UpdateStickyKeysFromPref,
690                    base::Unretained(this)));
691     pref_change_registrar_->Add(
692         prefs::kSpokenFeedbackEnabled,
693         base::Bind(&AccessibilityManager::UpdateSpokenFeedbackFromPref,
694                    base::Unretained(this)));
695     pref_change_registrar_->Add(
696         prefs::kHighContrastEnabled,
697         base::Bind(&AccessibilityManager::UpdateHighContrastFromPref,
698                    base::Unretained(this)));
699     pref_change_registrar_->Add(
700         prefs::kAutoclickEnabled,
701         base::Bind(&AccessibilityManager::UpdateAutoclickFromPref,
702                    base::Unretained(this)));
703     pref_change_registrar_->Add(
704         prefs::kAutoclickDelayMs,
705         base::Bind(&AccessibilityManager::UpdateAutoclickDelayFromPref,
706                    base::Unretained(this)));
707 
708     local_state_pref_change_registrar_.reset(new PrefChangeRegistrar);
709     local_state_pref_change_registrar_->Init(g_browser_process->local_state());
710     local_state_pref_change_registrar_->Add(
711         prefs::kApplicationLocale,
712         base::Bind(&AccessibilityManager::LocalePrefChanged,
713                    base::Unretained(this)));
714 
715     content::BrowserAccessibilityState::GetInstance()->AddHistogramCallback(
716         base::Bind(
717             &AccessibilityManager::UpdateChromeOSAccessibilityHistograms,
718             base::Unretained(this)));
719   }
720 
721   large_cursor_pref_handler_.HandleProfileChanged(profile_, profile);
722   spoken_feedback_pref_handler_.HandleProfileChanged(profile_, profile);
723   high_contrast_pref_handler_.HandleProfileChanged(profile_, profile);
724   autoclick_pref_handler_.HandleProfileChanged(profile_, profile);
725   autoclick_delay_pref_handler_.HandleProfileChanged(profile_, profile);
726 
727   if (!profile_ && profile)
728     CheckBrailleState();
729 
730   profile_ = profile;
731   UpdateLargeCursorFromPref();
732   UpdateStickyKeysFromPref();
733   UpdateSpokenFeedbackFromPref();
734   UpdateHighContrastFromPref();
735   UpdateAutoclickFromPref();
736   UpdateAutoclickDelayFromPref();
737 }
738 
ActiveUserChanged(const std::string & user_id)739 void AccessibilityManager::ActiveUserChanged(const std::string& user_id) {
740   SetProfile(ProfileManager::GetActiveUserProfile());
741 }
742 
SetProfileForTest(Profile * profile)743 void AccessibilityManager::SetProfileForTest(Profile* profile) {
744   SetProfile(profile);
745 }
746 
SetBrailleControllerForTest(BrailleController * controller)747 void AccessibilityManager::SetBrailleControllerForTest(
748     BrailleController* controller) {
749   g_braille_controller_for_test = controller;
750 }
751 
EnableSystemSounds(bool system_sounds_enabled)752 void AccessibilityManager::EnableSystemSounds(bool system_sounds_enabled) {
753   system_sounds_enabled_ = system_sounds_enabled;
754 }
755 
PlayShutdownSound()756 base::TimeDelta AccessibilityManager::PlayShutdownSound() {
757   if (!IsSpokenFeedbackEnabled() || !system_sounds_enabled_)
758     return base::TimeDelta();
759   system_sounds_enabled_ = false;
760   media::SoundsManager* manager = media::SoundsManager::Get();
761   manager->Play(SOUND_SHUTDOWN);
762   return manager->GetDuration(SOUND_SHUTDOWN);
763 }
764 
UpdateChromeOSAccessibilityHistograms()765 void AccessibilityManager::UpdateChromeOSAccessibilityHistograms() {
766   UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosSpokenFeedback",
767                         IsSpokenFeedbackEnabled());
768   UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosHighContrast",
769                         IsHighContrastEnabled());
770   UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosVirtualKeyboard",
771                         accessibility::IsVirtualKeyboardEnabled());
772   if (MagnificationManager::Get()) {
773     uint32 type = MagnificationManager::Get()->IsMagnifierEnabled() ?
774                       MagnificationManager::Get()->GetMagnifierType() : 0;
775     // '0' means magnifier is disabled.
776     UMA_HISTOGRAM_ENUMERATION("Accessibility.CrosScreenMagnifier",
777                               type,
778                               ash::kMaxMagnifierType + 1);
779   }
780   if (profile_) {
781     const PrefService* const prefs = profile_->GetPrefs();
782     UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosLargeCursor",
783                           prefs->GetBoolean(prefs::kLargeCursorEnabled));
784     UMA_HISTOGRAM_BOOLEAN(
785         "Accessibility.CrosAlwaysShowA11yMenu",
786         prefs->GetBoolean(prefs::kShouldAlwaysShowAccessibilityMenu));
787 
788     bool autoclick_enabled = prefs->GetBoolean(prefs::kAutoclickEnabled);
789     UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosAutoclick", autoclick_enabled);
790     if (autoclick_enabled) {
791       // We only want to log the autoclick delay if the user has actually
792       // enabled autoclick.
793       UMA_HISTOGRAM_CUSTOM_TIMES(
794           "Accessibility.CrosAutoclickDelay",
795           base::TimeDelta::FromMilliseconds(
796               prefs->GetInteger(prefs::kAutoclickDelayMs)),
797           base::TimeDelta::FromMilliseconds(1),
798           base::TimeDelta::FromMilliseconds(3000),
799           50);
800     }
801   }
802 }
803 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)804 void AccessibilityManager::Observe(
805     int type,
806     const content::NotificationSource& source,
807     const content::NotificationDetails& details) {
808   switch (type) {
809     case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
810       // Update |profile_| when entering the login screen.
811       Profile* profile = ProfileManager::GetActiveUserProfile();
812       if (ProfileHelper::IsSigninProfile(profile))
813         SetProfile(profile);
814       break;
815     }
816     case chrome::NOTIFICATION_SESSION_STARTED:
817       // Update |profile_| when entering a session.
818       SetProfile(ProfileManager::GetActiveUserProfile());
819 
820       // Ensure ChromeVox makes announcements at the start of new sessions.
821       should_speak_chrome_vox_announcements_on_user_screen_ = true;
822 
823       // Add a session state observer to be able to monitor session changes.
824       if (!session_state_observer_.get() && ash::Shell::HasInstance())
825         session_state_observer_.reset(
826             new ash::ScopedSessionStateObserver(this));
827       break;
828     case chrome::NOTIFICATION_PROFILE_DESTROYED: {
829       // Update |profile_| when exiting a session or shutting down.
830       Profile* profile = content::Source<Profile>(source).ptr();
831       if (profile_ == profile)
832         SetProfile(NULL);
833       break;
834     }
835     case chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED: {
836       bool is_screen_locked = *content::Details<bool>(details).ptr();
837       if (spoken_feedback_enabled_) {
838         if (is_screen_locked) {
839           LoadChromeVoxToLockScreen();
840 
841           // Status tray gets verbalized by user screen ChromeVox, so we need
842           // this as well.
843           LoadChromeVoxToUserScreen();
844         } else {
845           // Lock screen destroys its resources; no need for us to explicitly
846           // unload ChromeVox.
847           chrome_vox_loaded_on_lock_screen_ = false;
848 
849           // However, if spoken feedback was enabled, also enable it on the user
850           // screen.
851           LoadChromeVoxToUserScreen();
852         }
853       }
854       break;
855     }
856   }
857 }
858 
OnDisplayStateChanged(const DisplayState & display_state)859 void AccessibilityManager::OnDisplayStateChanged(
860     const DisplayState& display_state) {
861   if (display_state.available)
862     EnableSpokenFeedback(true, ash::A11Y_NOTIFICATION_SHOW);
863 }
864 
PostLoadChromeVox(Profile * profile)865 void AccessibilityManager::PostLoadChromeVox(Profile* profile) {
866   // Do any setup work needed immediately after ChromeVox actually loads.
867   PlaySound(SOUND_SPOKEN_FEEDBACK_ENABLED);
868     ExtensionAccessibilityEventRouter::GetInstance()->
869         OnChromeVoxLoadStateChanged(profile_,
870             IsSpokenFeedbackEnabled(),
871             chrome_vox_loaded_on_lock_screen_ ||
872                 should_speak_chrome_vox_announcements_on_user_screen_);
873 
874     should_speak_chrome_vox_announcements_on_user_screen_ =
875         chrome_vox_loaded_on_lock_screen_;
876 }
877 
PostUnloadChromeVox(Profile * profile)878 void AccessibilityManager::PostUnloadChromeVox(Profile* profile) {
879   // Do any teardown work needed immediately after ChromeVox actually unloads.
880   PlaySound(SOUND_SPOKEN_FEEDBACK_DISABLED);
881 }
882 
PlaySound(int sound_key) const883 void AccessibilityManager::PlaySound(int sound_key) const {
884   media::SoundsManager::Get()->Play(sound_key);
885 }
886 
887 }  // namespace chromeos
888