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.
5 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
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_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/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/profiles/profile.h"
36 #include "chrome/browser/profiles/profile_manager.h"
37 #include "chrome/common/chrome_paths.h"
38 #include "chrome/common/extensions/api/experimental_accessibility.h"
39 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
40 #include "chrome/common/pref_names.h"
41 #include "chromeos/audio/chromeos_sounds.h"
42 #include "chromeos/login/login_state.h"
43 #include "content/public/browser/browser_accessibility_state.h"
44 #include "content/public/browser/browser_thread.h"
45 #include "content/public/browser/notification_details.h"
46 #include "content/public/browser/notification_service.h"
47 #include "content/public/browser/notification_source.h"
48 #include "content/public/browser/render_process_host.h"
49 #include "content/public/browser/render_view_host.h"
50 #include "content/public/browser/web_contents.h"
51 #include "content/public/browser/web_ui.h"
52 #include "extensions/browser/extension_system.h"
53 #include "extensions/browser/file_reader.h"
54 #include "extensions/common/extension.h"
55 #include "extensions/common/extension_messages.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 #include "ui/keyboard/keyboard_controller.h"
63 #include "ui/keyboard/keyboard_util.h"
65 using content::BrowserThread;
66 using content::RenderViewHost;
67 using extensions::api::braille_display_private::BrailleController;
68 using extensions::api::braille_display_private::DisplayState;
74 static chromeos::AccessibilityManager* g_accessibility_manager = NULL;
76 static BrailleController* g_braille_controller_for_test = NULL;
78 BrailleController* GetBrailleController() {
79 return g_braille_controller_for_test
80 ? g_braille_controller_for_test
81 : BrailleController::GetInstance();
84 base::FilePath GetChromeVoxPath() {
86 if (!PathService::Get(chrome::DIR_RESOURCES, &path))
88 path = path.Append(extension_misc::kChromeVoxExtensionPath);
92 // Helper class that directly loads an extension's content scripts into
93 // all of the frames corresponding to a given RenderViewHost.
94 class ContentScriptLoader {
96 // Initialize the ContentScriptLoader with the ID of the extension
97 // and the RenderViewHost where the scripts should be loaded.
98 ContentScriptLoader(const std::string& extension_id,
99 int render_process_id,
101 : extension_id_(extension_id),
102 render_process_id_(render_process_id),
103 render_view_id_(render_view_id) {}
105 // Call this once with the ExtensionResource corresponding to each
106 // content script to be loaded.
107 void AppendScript(extensions::ExtensionResource resource) {
108 resources_.push(resource);
111 // Finally, call this method once to fetch all of the resources and
112 // load them. This method will delete this object when done.
114 if (resources_.empty()) {
119 extensions::ExtensionResource resource = resources_.front();
121 scoped_refptr<FileReader> reader(new FileReader(resource, base::Bind(
122 &ContentScriptLoader::OnFileLoaded, base::Unretained(this))));
127 void OnFileLoaded(bool success, const std::string& data) {
129 ExtensionMsg_ExecuteCode_Params params;
130 params.request_id = 0;
131 params.extension_id = extension_id_;
132 params.is_javascript = true;
134 params.run_at = extensions::UserScript::DOCUMENT_IDLE;
135 params.all_frames = true;
136 params.in_main_world = false;
138 RenderViewHost* render_view_host =
139 RenderViewHost::FromID(render_process_id_, render_view_id_);
140 if (render_view_host) {
141 render_view_host->Send(new ExtensionMsg_ExecuteCode(
142 render_view_host->GetRoutingID(), params));
148 std::string extension_id_;
149 int render_process_id_;
151 std::queue<extensions::ExtensionResource> resources_;
154 void LoadChromeVoxExtension(Profile* profile,
155 RenderViewHost* render_view_host) {
156 ExtensionService* extension_service =
157 extensions::ExtensionSystem::Get(profile)->extension_service();
158 std::string extension_id =
159 extension_service->component_loader()->AddChromeVoxExtension();
160 if (render_view_host) {
161 ExtensionService* extension_service =
162 extensions::ExtensionSystem::Get(profile)->extension_service();
163 const extensions::Extension* extension =
164 extension_service->extensions()->GetByID(extension_id);
166 // Set a flag to tell ChromeVox that it's just been enabled,
167 // so that it won't interrupt our speech feedback enabled message.
168 ExtensionMsg_ExecuteCode_Params params;
169 params.request_id = 0;
170 params.extension_id = extension->id();
171 params.is_javascript = true;
172 params.code = "window.INJECTED_AFTER_LOAD = true;";
173 params.run_at = extensions::UserScript::DOCUMENT_IDLE;
174 params.all_frames = true;
175 params.in_main_world = false;
176 render_view_host->Send(new ExtensionMsg_ExecuteCode(
177 render_view_host->GetRoutingID(), params));
179 // Inject ChromeVox' content scripts.
180 ContentScriptLoader* loader = new ContentScriptLoader(
181 extension->id(), render_view_host->GetProcess()->GetID(),
182 render_view_host->GetRoutingID());
184 const extensions::UserScriptList& content_scripts =
185 extensions::ContentScriptsInfo::GetContentScripts(extension);
186 for (size_t i = 0; i < content_scripts.size(); i++) {
187 const extensions::UserScript& script = content_scripts[i];
188 for (size_t j = 0; j < script.js_scripts().size(); ++j) {
189 const extensions::UserScript::File &file = script.js_scripts()[j];
190 extensions::ExtensionResource resource = extension->GetResource(
191 file.relative_path());
192 loader->AppendScript(resource);
195 loader->Run(); // It cleans itself up when done.
199 void UnloadChromeVoxExtension(Profile* profile) {
200 base::FilePath path = GetChromeVoxPath();
201 ExtensionService* extension_service =
202 extensions::ExtensionSystem::Get(profile)->extension_service();
203 extension_service->component_loader()->Remove(path);
208 ///////////////////////////////////////////////////////////////////////////////
209 // AccessibilityStatusEventDetails
211 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
212 AccessibilityNotificationType notification_type,
214 ash::AccessibilityNotificationVisibility notify)
215 : notification_type(notification_type),
217 magnifier_type(ash::kDefaultMagnifierType),
220 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
221 AccessibilityNotificationType notification_type,
223 ash::MagnifierType magnifier_type,
224 ash::AccessibilityNotificationVisibility notify)
225 : notification_type(notification_type),
227 magnifier_type(magnifier_type),
230 ///////////////////////////////////////////////////////////////////////////////
232 // AccessibilityManager::PrefHandler
234 AccessibilityManager::PrefHandler::PrefHandler(const char* pref_path)
235 : pref_path_(pref_path) {}
237 AccessibilityManager::PrefHandler::~PrefHandler() {}
239 void AccessibilityManager::PrefHandler::HandleProfileChanged(
240 Profile* previous_profile, Profile* current_profile) {
241 // Returns if the current profile is null.
242 if (!current_profile)
245 // If the user set a pref value on the login screen and is now starting a
246 // session with a new profile, copy the pref value to the profile.
247 if ((previous_profile &&
248 ProfileHelper::IsSigninProfile(previous_profile) &&
249 current_profile->IsNewProfile() &&
250 !ProfileHelper::IsSigninProfile(current_profile)) ||
251 // Special case for Guest mode:
252 // Guest mode launches a guest-mode browser process before session starts,
253 // so the previous profile is null.
254 (!previous_profile &&
255 current_profile->IsGuestSession())) {
256 // Returns if the pref has not been set by the user.
257 const PrefService::Preference* pref = ProfileHelper::GetSigninProfile()->
258 GetPrefs()->FindPreference(pref_path_);
259 if (!pref || !pref->IsUserControlled())
262 // Copy the pref value from the signin screen.
263 const base::Value* value_on_login = pref->GetValue();
264 PrefService* user_prefs = current_profile->GetPrefs();
265 user_prefs->Set(pref_path_, *value_on_login);
269 ///////////////////////////////////////////////////////////////////////////////
271 // AccessibilityManager
274 void AccessibilityManager::Initialize() {
275 CHECK(g_accessibility_manager == NULL);
276 g_accessibility_manager = new AccessibilityManager();
280 void AccessibilityManager::Shutdown() {
281 CHECK(g_accessibility_manager);
282 delete g_accessibility_manager;
283 g_accessibility_manager = NULL;
287 AccessibilityManager* AccessibilityManager::Get() {
288 return g_accessibility_manager;
291 AccessibilityManager::AccessibilityManager()
293 chrome_vox_loaded_on_lock_screen_(false),
294 chrome_vox_loaded_on_user_screen_(false),
295 large_cursor_pref_handler_(prefs::kLargeCursorEnabled),
296 spoken_feedback_pref_handler_(prefs::kSpokenFeedbackEnabled),
297 high_contrast_pref_handler_(prefs::kHighContrastEnabled),
298 autoclick_pref_handler_(prefs::kAutoclickEnabled),
299 autoclick_delay_pref_handler_(prefs::kAutoclickDelayMs),
300 virtual_keyboard_pref_handler_(prefs::kVirtualKeyboardEnabled),
301 large_cursor_enabled_(false),
302 sticky_keys_enabled_(false),
303 spoken_feedback_enabled_(false),
304 high_contrast_enabled_(false),
305 autoclick_enabled_(false),
306 autoclick_delay_ms_(ash::AutoclickController::kDefaultAutoclickDelayMs),
307 virtual_keyboard_enabled_(false),
308 spoken_feedback_notification_(ash::A11Y_NOTIFICATION_NONE),
309 weak_ptr_factory_(this),
310 should_speak_chrome_vox_announcements_on_user_screen_(true),
311 system_sounds_enabled_(false),
312 braille_display_connected_(false),
313 scoped_braille_observer_(this) {
314 notification_registrar_.Add(this,
315 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
316 content::NotificationService::AllSources());
317 notification_registrar_.Add(this,
318 chrome::NOTIFICATION_SESSION_STARTED,
319 content::NotificationService::AllSources());
320 notification_registrar_.Add(this,
321 chrome::NOTIFICATION_PROFILE_DESTROYED,
322 content::NotificationService::AllSources());
323 notification_registrar_.Add(this,
324 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED,
325 content::NotificationService::AllSources());
327 input_method::InputMethodManager::Get()->AddObserver(this);
329 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
330 media::SoundsManager* manager = media::SoundsManager::Get();
331 manager->Initialize(SOUND_SHUTDOWN,
332 bundle.GetRawDataResource(IDR_SOUND_SHUTDOWN_WAV));
334 SOUND_SPOKEN_FEEDBACK_ENABLED,
335 bundle.GetRawDataResource(IDR_SOUND_SPOKEN_FEEDBACK_ENABLED_WAV));
337 SOUND_SPOKEN_FEEDBACK_DISABLED,
338 bundle.GetRawDataResource(IDR_SOUND_SPOKEN_FEEDBACK_DISABLED_WAV));
341 AccessibilityManager::~AccessibilityManager() {
342 CHECK(this == g_accessibility_manager);
343 AccessibilityStatusEventDetails details(
344 ACCESSIBILITY_MANAGER_SHUTDOWN,
346 ash::A11Y_NOTIFICATION_NONE);
347 NotifyAccessibilityStatusChanged(details);
348 input_method::InputMethodManager::Get()->RemoveObserver(this);
351 bool AccessibilityManager::ShouldShowAccessibilityMenu() {
352 // If any of the loaded profiles has an accessibility feature turned on - or
353 // enforced to always show the menu - we return true to show the menu.
354 std::vector<Profile*> profiles =
355 g_browser_process->profile_manager()->GetLoadedProfiles();
356 for (std::vector<Profile*>::iterator it = profiles.begin();
357 it != profiles.end();
359 PrefService* pref_service = (*it)->GetPrefs();
360 if (pref_service->GetBoolean(prefs::kStickyKeysEnabled) ||
361 pref_service->GetBoolean(prefs::kLargeCursorEnabled) ||
362 pref_service->GetBoolean(prefs::kSpokenFeedbackEnabled) ||
363 pref_service->GetBoolean(prefs::kHighContrastEnabled) ||
364 pref_service->GetBoolean(prefs::kAutoclickEnabled) ||
365 pref_service->GetBoolean(prefs::kShouldAlwaysShowAccessibilityMenu) ||
366 pref_service->GetBoolean(prefs::kScreenMagnifierEnabled) ||
367 pref_service->GetBoolean(prefs::kVirtualKeyboardEnabled))
373 bool AccessibilityManager::ShouldEnableCursorCompositing() {
374 // TODO(hshi): re-enable this on trunk after fixing issues. See
375 // http://crbug.com/362693, http://crosbug.com/p/28034.
379 void AccessibilityManager::EnableLargeCursor(bool enabled) {
383 PrefService* pref_service = profile_->GetPrefs();
384 pref_service->SetBoolean(prefs::kLargeCursorEnabled, enabled);
385 pref_service->CommitPendingWrite();
388 void AccessibilityManager::UpdateLargeCursorFromPref() {
393 profile_->GetPrefs()->GetBoolean(prefs::kLargeCursorEnabled);
395 if (large_cursor_enabled_ == enabled)
398 large_cursor_enabled_ = enabled;
400 AccessibilityStatusEventDetails details(
401 ACCESSIBILITY_TOGGLE_LARGE_CURSOR,
403 ash::A11Y_NOTIFICATION_NONE);
405 NotifyAccessibilityStatusChanged(details);
408 // Large cursor is implemented only in ash.
409 ash::Shell::GetInstance()->cursor_manager()->SetCursorSet(
410 enabled ? ui::CURSOR_SET_LARGE : ui::CURSOR_SET_NORMAL);
413 #if defined(OS_CHROMEOS)
414 ash::Shell::GetInstance()->SetCursorCompositingEnabled(
415 ShouldEnableCursorCompositing());
419 bool AccessibilityManager::IsIncognitoAllowed() {
420 UserManager* user_manager = UserManager::Get();
421 // Supervised users can't create incognito-mode windows.
422 return !(user_manager->IsLoggedInAsLocallyManagedUser());
425 bool AccessibilityManager::IsLargeCursorEnabled() {
426 return large_cursor_enabled_;
429 void AccessibilityManager::EnableStickyKeys(bool enabled) {
432 PrefService* pref_service = profile_->GetPrefs();
433 pref_service->SetBoolean(prefs::kStickyKeysEnabled, enabled);
434 pref_service->CommitPendingWrite();
437 bool AccessibilityManager::IsStickyKeysEnabled() {
438 return sticky_keys_enabled_;
441 void AccessibilityManager::UpdateStickyKeysFromPref() {
446 profile_->GetPrefs()->GetBoolean(prefs::kStickyKeysEnabled);
448 if (sticky_keys_enabled_ == enabled)
451 sticky_keys_enabled_ = enabled;
453 // Sticky keys is implemented only in ash.
454 ash::Shell::GetInstance()->sticky_keys_controller()->Enable(enabled);
458 void AccessibilityManager::EnableSpokenFeedback(
460 ash::AccessibilityNotificationVisibility notify) {
464 ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(
465 enabled ? ash::UMA_STATUS_AREA_ENABLE_SPOKEN_FEEDBACK
466 : ash::UMA_STATUS_AREA_DISABLE_SPOKEN_FEEDBACK);
468 spoken_feedback_notification_ = notify;
470 PrefService* pref_service = profile_->GetPrefs();
471 pref_service->SetBoolean(
472 prefs::kSpokenFeedbackEnabled, enabled);
473 pref_service->CommitPendingWrite();
475 spoken_feedback_notification_ = ash::A11Y_NOTIFICATION_NONE;
478 void AccessibilityManager::UpdateSpokenFeedbackFromPref() {
483 profile_->GetPrefs()->GetBoolean(prefs::kSpokenFeedbackEnabled);
485 if (spoken_feedback_enabled_ == enabled)
488 spoken_feedback_enabled_ = enabled;
490 ExtensionAccessibilityEventRouter::GetInstance()->
491 SetAccessibilityEnabled(enabled);
493 AccessibilityStatusEventDetails details(
494 ACCESSIBILITY_TOGGLE_SPOKEN_FEEDBACK,
496 spoken_feedback_notification_);
498 NotifyAccessibilityStatusChanged(details);
507 void AccessibilityManager::LoadChromeVox() {
508 ScreenLocker* screen_locker = ScreenLocker::default_screen_locker();
509 if (screen_locker && screen_locker->locked()) {
510 // If on the lock screen, loads ChromeVox only to the lock screen as for
511 // now. On unlock, it will be loaded to the user screen.
512 // (see. AccessibilityManager::Observe())
513 LoadChromeVoxToLockScreen();
515 LoadChromeVoxToUserScreen();
517 PostLoadChromeVox(profile_);
520 void AccessibilityManager::LoadChromeVoxToUserScreen() {
521 if (chrome_vox_loaded_on_user_screen_)
524 // Determine whether an OOBE screen is currently being shown. If so,
525 // ChromeVox will be injected directly into that screen.
526 content::WebUI* login_web_ui = NULL;
528 if (ProfileHelper::IsSigninProfile(profile_)) {
529 LoginDisplayHost* login_display_host = LoginDisplayHostImpl::default_host();
530 if (login_display_host) {
531 WebUILoginView* web_ui_login_view =
532 login_display_host->GetWebUILoginView();
533 if (web_ui_login_view)
534 login_web_ui = web_ui_login_view->GetWebUI();
538 LoadChromeVoxExtension(profile_, login_web_ui ?
539 login_web_ui->GetWebContents()->GetRenderViewHost() : NULL);
540 chrome_vox_loaded_on_user_screen_ = true;
543 void AccessibilityManager::LoadChromeVoxToLockScreen() {
544 if (chrome_vox_loaded_on_lock_screen_)
547 ScreenLocker* screen_locker = ScreenLocker::default_screen_locker();
548 if (screen_locker && screen_locker->locked()) {
549 content::WebUI* lock_web_ui = screen_locker->GetAssociatedWebUI();
551 Profile* profile = Profile::FromWebUI(lock_web_ui);
552 LoadChromeVoxExtension(profile,
553 lock_web_ui->GetWebContents()->GetRenderViewHost());
554 chrome_vox_loaded_on_lock_screen_ = true;
559 void AccessibilityManager::UnloadChromeVox() {
560 if (chrome_vox_loaded_on_lock_screen_)
561 UnloadChromeVoxFromLockScreen();
563 if (chrome_vox_loaded_on_user_screen_) {
564 UnloadChromeVoxExtension(profile_);
565 chrome_vox_loaded_on_user_screen_ = false;
568 PostUnloadChromeVox(profile_);
571 void AccessibilityManager::UnloadChromeVoxFromLockScreen() {
572 // Lock screen uses the signin progile.
573 Profile* signin_profile = ProfileHelper::GetSigninProfile();
574 UnloadChromeVoxExtension(signin_profile);
575 chrome_vox_loaded_on_lock_screen_ = false;
578 bool AccessibilityManager::IsSpokenFeedbackEnabled() {
579 return spoken_feedback_enabled_;
582 void AccessibilityManager::ToggleSpokenFeedback(
583 ash::AccessibilityNotificationVisibility notify) {
584 EnableSpokenFeedback(!IsSpokenFeedbackEnabled(), notify);
587 void AccessibilityManager::EnableHighContrast(bool enabled) {
591 PrefService* pref_service = profile_->GetPrefs();
592 pref_service->SetBoolean(prefs::kHighContrastEnabled, enabled);
593 pref_service->CommitPendingWrite();
596 void AccessibilityManager::UpdateHighContrastFromPref() {
601 profile_->GetPrefs()->GetBoolean(prefs::kHighContrastEnabled);
603 if (high_contrast_enabled_ == enabled)
606 high_contrast_enabled_ = enabled;
608 AccessibilityStatusEventDetails details(
609 ACCESSIBILITY_TOGGLE_HIGH_CONTRAST_MODE,
611 ash::A11Y_NOTIFICATION_NONE);
613 NotifyAccessibilityStatusChanged(details);
616 ash::Shell::GetInstance()->high_contrast_controller()->SetEnabled(enabled);
619 #if defined(OS_CHROMEOS)
620 ash::Shell::GetInstance()->SetCursorCompositingEnabled(
621 ShouldEnableCursorCompositing());
625 void AccessibilityManager::OnLocaleChanged() {
629 if (!IsSpokenFeedbackEnabled())
632 // If the system locale changes and spoken feedback is enabled,
633 // reload ChromeVox so that it switches its internal translations
634 // to the new language.
635 EnableSpokenFeedback(false, ash::A11Y_NOTIFICATION_NONE);
636 EnableSpokenFeedback(true, ash::A11Y_NOTIFICATION_NONE);
639 bool AccessibilityManager::IsHighContrastEnabled() {
640 return high_contrast_enabled_;
643 void AccessibilityManager::EnableAutoclick(bool enabled) {
647 PrefService* pref_service = profile_->GetPrefs();
648 pref_service->SetBoolean(prefs::kAutoclickEnabled, enabled);
649 pref_service->CommitPendingWrite();
652 bool AccessibilityManager::IsAutoclickEnabled() {
653 return autoclick_enabled_;
656 void AccessibilityManager::UpdateAutoclickFromPref() {
658 profile_->GetPrefs()->GetBoolean(prefs::kAutoclickEnabled);
660 if (autoclick_enabled_ == enabled)
662 autoclick_enabled_ = enabled;
665 ash::Shell::GetInstance()->autoclick_controller()->SetEnabled(enabled);
669 void AccessibilityManager::SetAutoclickDelay(int delay_ms) {
673 PrefService* pref_service = profile_->GetPrefs();
674 pref_service->SetInteger(prefs::kAutoclickDelayMs, delay_ms);
675 pref_service->CommitPendingWrite();
678 int AccessibilityManager::GetAutoclickDelay() const {
679 return autoclick_delay_ms_;
682 void AccessibilityManager::UpdateAutoclickDelayFromPref() {
683 int autoclick_delay_ms =
684 profile_->GetPrefs()->GetInteger(prefs::kAutoclickDelayMs);
686 if (autoclick_delay_ms == autoclick_delay_ms_)
688 autoclick_delay_ms_ = autoclick_delay_ms;
691 ash::Shell::GetInstance()->autoclick_controller()->SetAutoclickDelay(
692 autoclick_delay_ms_);
696 void AccessibilityManager::EnableVirtualKeyboard(bool enabled) {
700 PrefService* pref_service = profile_->GetPrefs();
701 pref_service->SetBoolean(prefs::kVirtualKeyboardEnabled, enabled);
702 pref_service->CommitPendingWrite();
705 bool AccessibilityManager::IsVirtualKeyboardEnabled() {
706 return virtual_keyboard_enabled_;
709 void AccessibilityManager::UpdateVirtualKeyboardFromPref() {
714 profile_->GetPrefs()->GetBoolean(prefs::kVirtualKeyboardEnabled);
716 if (virtual_keyboard_enabled_ == enabled)
718 virtual_keyboard_enabled_ = enabled;
720 AccessibilityStatusEventDetails details(
721 ACCESSIBILITY_TOGGLE_VIRTUAL_KEYBOARD,
723 ash::A11Y_NOTIFICATION_NONE);
725 NotifyAccessibilityStatusChanged(details);
728 keyboard::SetAccessibilityKeyboardEnabled(enabled);
730 ash::Shell::GetInstance()->CreateKeyboard();
731 else if (!keyboard::IsKeyboardEnabled())
732 ash::Shell::GetInstance()->DeactivateKeyboard();
736 bool AccessibilityManager::IsBrailleDisplayConnected() const {
737 return braille_display_connected_;
740 void AccessibilityManager::CheckBrailleState() {
741 BrailleController* braille_controller = GetBrailleController();
742 if (!scoped_braille_observer_.IsObserving(braille_controller))
743 scoped_braille_observer_.Add(braille_controller);
744 BrowserThread::PostTaskAndReplyWithResult(
747 base::Bind(&BrailleController::GetDisplayState,
748 base::Unretained(braille_controller)),
749 base::Bind(&AccessibilityManager::ReceiveBrailleDisplayState,
750 weak_ptr_factory_.GetWeakPtr()));
753 void AccessibilityManager::ReceiveBrailleDisplayState(
754 scoped_ptr<extensions::api::braille_display_private::DisplayState> state) {
755 OnDisplayStateChanged(*state);
758 // Overridden from InputMethodManager::Observer.
759 void AccessibilityManager::InputMethodChanged(
760 input_method::InputMethodManager* manager,
763 // Sticky keys is implemented only in ash.
764 ash::Shell::GetInstance()->sticky_keys_controller()->SetModifiersEnabled(
765 manager->IsISOLevel5ShiftUsedByCurrentInputMethod(),
766 manager->IsAltGrUsedByCurrentInputMethod());
770 void AccessibilityManager::SetProfile(Profile* profile) {
771 pref_change_registrar_.reset();
772 local_state_pref_change_registrar_.reset();
775 // TODO(yoshiki): Move following code to PrefHandler.
776 pref_change_registrar_.reset(new PrefChangeRegistrar);
777 pref_change_registrar_->Init(profile->GetPrefs());
778 pref_change_registrar_->Add(
779 prefs::kLargeCursorEnabled,
780 base::Bind(&AccessibilityManager::UpdateLargeCursorFromPref,
781 base::Unretained(this)));
782 pref_change_registrar_->Add(
783 prefs::kStickyKeysEnabled,
784 base::Bind(&AccessibilityManager::UpdateStickyKeysFromPref,
785 base::Unretained(this)));
786 pref_change_registrar_->Add(
787 prefs::kSpokenFeedbackEnabled,
788 base::Bind(&AccessibilityManager::UpdateSpokenFeedbackFromPref,
789 base::Unretained(this)));
790 pref_change_registrar_->Add(
791 prefs::kHighContrastEnabled,
792 base::Bind(&AccessibilityManager::UpdateHighContrastFromPref,
793 base::Unretained(this)));
794 pref_change_registrar_->Add(
795 prefs::kAutoclickEnabled,
796 base::Bind(&AccessibilityManager::UpdateAutoclickFromPref,
797 base::Unretained(this)));
798 pref_change_registrar_->Add(
799 prefs::kAutoclickDelayMs,
800 base::Bind(&AccessibilityManager::UpdateAutoclickDelayFromPref,
801 base::Unretained(this)));
802 pref_change_registrar_->Add(
803 prefs::kVirtualKeyboardEnabled,
804 base::Bind(&AccessibilityManager::UpdateVirtualKeyboardFromPref,
805 base::Unretained(this)));
807 local_state_pref_change_registrar_.reset(new PrefChangeRegistrar);
808 local_state_pref_change_registrar_->Init(g_browser_process->local_state());
809 local_state_pref_change_registrar_->Add(
810 prefs::kApplicationLocale,
811 base::Bind(&AccessibilityManager::OnLocaleChanged,
812 base::Unretained(this)));
814 content::BrowserAccessibilityState::GetInstance()->AddHistogramCallback(
816 &AccessibilityManager::UpdateChromeOSAccessibilityHistograms,
817 base::Unretained(this)));
820 large_cursor_pref_handler_.HandleProfileChanged(profile_, profile);
821 spoken_feedback_pref_handler_.HandleProfileChanged(profile_, profile);
822 high_contrast_pref_handler_.HandleProfileChanged(profile_, profile);
823 autoclick_pref_handler_.HandleProfileChanged(profile_, profile);
824 autoclick_delay_pref_handler_.HandleProfileChanged(profile_, profile);
825 virtual_keyboard_pref_handler_.HandleProfileChanged(profile_, profile);
827 bool had_profile = (profile_ != NULL);
830 if (!had_profile && profile)
833 UpdateLargeCursorFromPref();
834 UpdateStickyKeysFromPref();
835 UpdateSpokenFeedbackFromPref();
836 UpdateHighContrastFromPref();
837 UpdateAutoclickFromPref();
838 UpdateAutoclickDelayFromPref();
839 UpdateVirtualKeyboardFromPref();
842 void AccessibilityManager::ActiveUserChanged(const std::string& user_id) {
843 SetProfile(ProfileManager::GetActiveUserProfile());
846 void AccessibilityManager::SetProfileForTest(Profile* profile) {
850 void AccessibilityManager::SetBrailleControllerForTest(
851 BrailleController* controller) {
852 g_braille_controller_for_test = controller;
855 void AccessibilityManager::EnableSystemSounds(bool system_sounds_enabled) {
856 system_sounds_enabled_ = system_sounds_enabled;
859 base::TimeDelta AccessibilityManager::PlayShutdownSound() {
860 if (!system_sounds_enabled_)
861 return base::TimeDelta();
862 system_sounds_enabled_ = false;
863 if (!ash::PlaySystemSoundIfSpokenFeedback(SOUND_SHUTDOWN))
864 return base::TimeDelta();
865 return media::SoundsManager::Get()->GetDuration(SOUND_SHUTDOWN);
868 void AccessibilityManager::InjectChromeVox(RenderViewHost* render_view_host) {
869 LoadChromeVoxExtension(profile_, render_view_host);
872 scoped_ptr<AccessibilityStatusSubscription>
873 AccessibilityManager::RegisterCallback(
874 const AccessibilityStatusCallback& cb) {
875 return callback_list_.Add(cb);
878 void AccessibilityManager::NotifyAccessibilityStatusChanged(
879 AccessibilityStatusEventDetails& details) {
880 callback_list_.Notify(details);
883 void AccessibilityManager::UpdateChromeOSAccessibilityHistograms() {
884 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosSpokenFeedback",
885 IsSpokenFeedbackEnabled());
886 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosHighContrast",
887 IsHighContrastEnabled());
888 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosVirtualKeyboard",
889 IsVirtualKeyboardEnabled());
890 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosStickyKeys", IsStickyKeysEnabled());
891 if (MagnificationManager::Get()) {
892 uint32 type = MagnificationManager::Get()->IsMagnifierEnabled() ?
893 MagnificationManager::Get()->GetMagnifierType() : 0;
894 // '0' means magnifier is disabled.
895 UMA_HISTOGRAM_ENUMERATION("Accessibility.CrosScreenMagnifier",
897 ash::kMaxMagnifierType + 1);
900 const PrefService* const prefs = profile_->GetPrefs();
901 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosLargeCursor",
902 prefs->GetBoolean(prefs::kLargeCursorEnabled));
903 UMA_HISTOGRAM_BOOLEAN(
904 "Accessibility.CrosAlwaysShowA11yMenu",
905 prefs->GetBoolean(prefs::kShouldAlwaysShowAccessibilityMenu));
907 bool autoclick_enabled = prefs->GetBoolean(prefs::kAutoclickEnabled);
908 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosAutoclick", autoclick_enabled);
909 if (autoclick_enabled) {
910 // We only want to log the autoclick delay if the user has actually
911 // enabled autoclick.
912 UMA_HISTOGRAM_CUSTOM_TIMES(
913 "Accessibility.CrosAutoclickDelay",
914 base::TimeDelta::FromMilliseconds(
915 prefs->GetInteger(prefs::kAutoclickDelayMs)),
916 base::TimeDelta::FromMilliseconds(1),
917 base::TimeDelta::FromMilliseconds(3000),
923 void AccessibilityManager::Observe(
925 const content::NotificationSource& source,
926 const content::NotificationDetails& details) {
928 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
929 // Update |profile_| when entering the login screen.
930 Profile* profile = ProfileManager::GetActiveUserProfile();
931 if (ProfileHelper::IsSigninProfile(profile))
935 case chrome::NOTIFICATION_SESSION_STARTED:
936 // Update |profile_| when entering a session.
937 SetProfile(ProfileManager::GetActiveUserProfile());
939 // Ensure ChromeVox makes announcements at the start of new sessions.
940 should_speak_chrome_vox_announcements_on_user_screen_ = true;
942 // Add a session state observer to be able to monitor session changes.
943 if (!session_state_observer_.get() && ash::Shell::HasInstance())
944 session_state_observer_.reset(
945 new ash::ScopedSessionStateObserver(this));
947 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
948 // Update |profile_| when exiting a session or shutting down.
949 Profile* profile = content::Source<Profile>(source).ptr();
950 if (profile_ == profile)
954 case chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED: {
955 bool is_screen_locked = *content::Details<bool>(details).ptr();
956 if (spoken_feedback_enabled_) {
957 if (is_screen_locked) {
958 LoadChromeVoxToLockScreen();
960 // Status tray gets verbalized by user screen ChromeVox, so we need
962 LoadChromeVoxToUserScreen();
964 // Lock screen destroys its resources; no need for us to explicitly
966 chrome_vox_loaded_on_lock_screen_ = false;
968 // However, if spoken feedback was enabled, also enable it on the user
970 LoadChromeVoxToUserScreen();
978 void AccessibilityManager::OnDisplayStateChanged(
979 const DisplayState& display_state) {
980 braille_display_connected_ = display_state.available;
981 if (braille_display_connected_)
982 EnableSpokenFeedback(true, ash::A11Y_NOTIFICATION_SHOW);
984 AccessibilityStatusEventDetails details(
985 ACCESSIBILITY_BRAILLE_DISPLAY_CONNECTION_STATE_CHANGED,
986 braille_display_connected_,
987 ash::A11Y_NOTIFICATION_SHOW);
988 NotifyAccessibilityStatusChanged(details);
991 void AccessibilityManager::PostLoadChromeVox(Profile* profile) {
992 // Do any setup work needed immediately after ChromeVox actually loads.
993 if (system_sounds_enabled_)
994 ash::PlaySystemSoundAlways(SOUND_SPOKEN_FEEDBACK_ENABLED);
996 ExtensionAccessibilityEventRouter::GetInstance()->
997 OnChromeVoxLoadStateChanged(profile_,
998 IsSpokenFeedbackEnabled(),
999 chrome_vox_loaded_on_lock_screen_ ||
1000 should_speak_chrome_vox_announcements_on_user_screen_);
1002 should_speak_chrome_vox_announcements_on_user_screen_ =
1003 chrome_vox_loaded_on_lock_screen_;
1006 void AccessibilityManager::PostUnloadChromeVox(Profile* profile) {
1007 // Do any teardown work needed immediately after ChromeVox actually unloads.
1008 if (system_sounds_enabled_)
1009 ash::PlaySystemSoundAlways(SOUND_SPOKEN_FEEDBACK_DISABLED);
1012 } // namespace chromeos