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 63using content::BrowserThread; 64using content::RenderViewHost; 65using extensions::api::braille_display_private::BrailleController; 66using extensions::api::braille_display_private::DisplayState; 67 68namespace chromeos { 69 70namespace { 71 72static chromeos::AccessibilityManager* g_accessibility_manager = NULL; 73 74static BrailleController* g_braille_controller_for_test = NULL; 75 76BrailleController* GetBrailleController() { 77 return g_braille_controller_for_test 78 ? g_braille_controller_for_test 79 : BrailleController::GetInstance(); 80} 81 82base::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. 92class ContentScriptLoader { 93 public: 94 // Initialize the ContentScriptLoader with the ID of the extension 95 // and the RenderViewHost where the scripts should be loaded. 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. 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. 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: 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 152void 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 198void 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 210AccessibilityStatusEventDetails::AccessibilityStatusEventDetails( 211 bool enabled, 212 ash::AccessibilityNotificationVisibility notify) 213 : enabled(enabled), 214 magnifier_type(ash::kDefaultMagnifierType), 215 notify(notify) {} 216 217AccessibilityStatusEventDetails::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 229AccessibilityManager::PrefHandler::PrefHandler(const char* pref_path) 230 : pref_path_(pref_path) {} 231 232AccessibilityManager::PrefHandler::~PrefHandler() {} 233 234void 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 269void AccessibilityManager::Initialize() { 270 CHECK(g_accessibility_manager == NULL); 271 g_accessibility_manager = new AccessibilityManager(); 272} 273 274// static 275void AccessibilityManager::Shutdown() { 276 CHECK(g_accessibility_manager); 277 delete g_accessibility_manager; 278 g_accessibility_manager = NULL; 279} 280 281// static 282AccessibilityManager* AccessibilityManager::Get() { 283 return g_accessibility_manager; 284} 285 286AccessibilityManager::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 332AccessibilityManager::~AccessibilityManager() { 333 CHECK(this == g_accessibility_manager); 334} 335 336bool 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 357void 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 366void 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 391bool AccessibilityManager::IsIncognitoAllowed() { 392 UserManager* user_manager = UserManager::Get(); 393 // Supervised users can't create incognito-mode windows. 394 return !(user_manager->IsLoggedInAsLocallyManagedUser()); 395} 396 397bool AccessibilityManager::IsLargeCursorEnabled() { 398 return large_cursor_enabled_; 399} 400 401void 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 409bool AccessibilityManager::IsStickyKeysEnabled() { 410 return sticky_keys_enabled_; 411} 412 413void 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 430void 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 450void 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 479void 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 492void 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 514void 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 529void 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 541void 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 548bool AccessibilityManager::IsSpokenFeedbackEnabled() { 549 return spoken_feedback_enabled_; 550} 551 552void AccessibilityManager::ToggleSpokenFeedback( 553 ash::AccessibilityNotificationVisibility notify) { 554 EnableSpokenFeedback(!IsSpokenFeedbackEnabled(), notify); 555} 556 557void 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 566void 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 589void 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 603bool AccessibilityManager::IsHighContrastEnabled() { 604 return high_contrast_enabled_; 605} 606 607void 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 616bool AccessibilityManager::IsAutoclickEnabled() { 617 return autoclick_enabled_; 618} 619 620void 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 633void 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 642int AccessibilityManager::GetAutoclickDelay() const { 643 return autoclick_delay_ms_; 644} 645 646void 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 660void 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 669void AccessibilityManager::ReceiveBrailleDisplayState( 670 scoped_ptr<extensions::api::braille_display_private::DisplayState> state) { 671 OnDisplayStateChanged(*state); 672} 673 674 675void 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 739void AccessibilityManager::ActiveUserChanged(const std::string& user_id) { 740 SetProfile(ProfileManager::GetActiveUserProfile()); 741} 742 743void AccessibilityManager::SetProfileForTest(Profile* profile) { 744 SetProfile(profile); 745} 746 747void AccessibilityManager::SetBrailleControllerForTest( 748 BrailleController* controller) { 749 g_braille_controller_for_test = controller; 750} 751 752void AccessibilityManager::EnableSystemSounds(bool system_sounds_enabled) { 753 system_sounds_enabled_ = system_sounds_enabled; 754} 755 756base::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 765void 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 804void 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 859void AccessibilityManager::OnDisplayStateChanged( 860 const DisplayState& display_state) { 861 if (display_state.available) 862 EnableSpokenFeedback(true, ash::A11Y_NOTIFICATION_SHOW); 863} 864 865void 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 878void AccessibilityManager::PostUnloadChromeVox(Profile* profile) { 879 // Do any teardown work needed immediately after ChromeVox actually unloads. 880 PlaySound(SOUND_SPOKEN_FEEDBACK_DISABLED); 881} 882 883void AccessibilityManager::PlaySound(int sound_key) const { 884 media::SoundsManager::Get()->Play(sound_key); 885} 886 887} // namespace chromeos 888