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 "ash/wm/event_rewriter_event_filter.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/memory/singleton.h"
18 #include "base/metrics/histogram.h"
19 #include "base/path_service.h"
20 #include "base/prefs/pref_member.h"
21 #include "base/prefs/pref_service.h"
22 #include "base/time/time.h"
23 #include "base/values.h"
24 #include "chrome/browser/accessibility/accessibility_extension_api.h"
25 #include "chrome/browser/browser_process.h"
26 #include "chrome/browser/chrome_notification_types.h"
27 #include "chrome/browser/chromeos/accessibility/magnification_manager.h"
28 #include "chrome/browser/chromeos/login/login_display_host.h"
29 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
30 #include "chrome/browser/chromeos/login/screen_locker.h"
31 #include "chrome/browser/chromeos/login/user_manager.h"
32 #include "chrome/browser/chromeos/login/webui_login_view.h"
33 #include "chrome/browser/chromeos/profiles/profile_helper.h"
34 #include "chrome/browser/extensions/component_loader.h"
35 #include "chrome/browser/extensions/extension_service.h"
36 #include "chrome/browser/extensions/extension_system.h"
37 #include "chrome/browser/profiles/profile.h"
38 #include "chrome/browser/profiles/profile_manager.h"
39 #include "chrome/browser/speech/tts_controller.h"
40 #include "chrome/common/chrome_paths.h"
41 #include "chrome/common/extensions/api/experimental_accessibility.h"
42 #include "chrome/common/extensions/extension_messages.h"
43 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
44 #include "chrome/common/pref_names.h"
45 #include "chromeos/audio/chromeos_sounds.h"
46 #include "chromeos/login/login_state.h"
47 #include "content/public/browser/browser_accessibility_state.h"
48 #include "content/public/browser/browser_thread.h"
49 #include "content/public/browser/notification_details.h"
50 #include "content/public/browser/notification_service.h"
51 #include "content/public/browser/notification_source.h"
52 #include "content/public/browser/render_process_host.h"
53 #include "content/public/browser/render_view_host.h"
54 #include "content/public/browser/web_contents.h"
55 #include "content/public/browser/web_ui.h"
56 #include "extensions/browser/file_reader.h"
57 #include "extensions/common/extension.h"
58 #include "extensions/common/extension_resource.h"
59 #include "grit/browser_resources.h"
60 #include "grit/generated_resources.h"
61 #include "media/audio/sounds/sounds_manager.h"
62 #include "ui/base/l10n/l10n_util.h"
63 #include "ui/base/resource/resource_bundle.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
, content::WebUI
* login_web_ui
) {
155 ExtensionService
* extension_service
=
156 extensions::ExtensionSystem::Get(profile
)->extension_service();
157 base::FilePath path
= GetChromeVoxPath();
158 std::string extension_id
=
159 extension_service
->component_loader()->Add(IDR_CHROMEVOX_MANIFEST
,
162 ExtensionService
* extension_service
=
163 extensions::ExtensionSystem::Get(profile
)->extension_service();
164 const extensions::Extension
* extension
=
165 extension_service
->extensions()->GetByID(extension_id
);
167 RenderViewHost
* render_view_host
=
168 login_web_ui
->GetWebContents()->GetRenderViewHost();
169 // Set a flag to tell ChromeVox that it's just been enabled,
170 // so that it won't interrupt our speech feedback enabled message.
171 ExtensionMsg_ExecuteCode_Params params
;
172 params
.request_id
= 0;
173 params
.extension_id
= extension
->id();
174 params
.is_javascript
= true;
175 params
.code
= "window.INJECTED_AFTER_LOAD = true;";
176 params
.run_at
= extensions::UserScript::DOCUMENT_IDLE
;
177 params
.all_frames
= true;
178 params
.in_main_world
= false;
179 render_view_host
->Send(new ExtensionMsg_ExecuteCode(
180 render_view_host
->GetRoutingID(), params
));
182 // Inject ChromeVox' content scripts.
183 ContentScriptLoader
* loader
= new ContentScriptLoader(
184 extension
->id(), render_view_host
->GetProcess()->GetID(),
185 render_view_host
->GetRoutingID());
187 const extensions::UserScriptList
& content_scripts
=
188 extensions::ContentScriptsInfo::GetContentScripts(extension
);
189 for (size_t i
= 0; i
< content_scripts
.size(); i
++) {
190 const extensions::UserScript
& script
= content_scripts
[i
];
191 for (size_t j
= 0; j
< script
.js_scripts().size(); ++j
) {
192 const extensions::UserScript::File
&file
= script
.js_scripts()[j
];
193 extensions::ExtensionResource resource
= extension
->GetResource(
194 file
.relative_path());
195 loader
->AppendScript(resource
);
198 loader
->Run(); // It cleans itself up when done.
202 void UnloadChromeVoxExtension(Profile
* profile
) {
203 base::FilePath path
= GetChromeVoxPath();
204 ExtensionService
* extension_service
=
205 extensions::ExtensionSystem::Get(profile
)->extension_service();
206 extension_service
->component_loader()->Remove(path
);
211 ///////////////////////////////////////////////////////////////////////////////
212 // AccessibilityStatusEventDetails
214 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
216 ash::AccessibilityNotificationVisibility notify
)
218 magnifier_type(ash::kDefaultMagnifierType
),
221 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
223 ash::MagnifierType magnifier_type
,
224 ash::AccessibilityNotificationVisibility notify
)
226 magnifier_type(magnifier_type
),
229 ///////////////////////////////////////////////////////////////////////////////
231 // AccessibilityManager::PrefHandler
233 AccessibilityManager::PrefHandler::PrefHandler(const char* pref_path
)
234 : pref_path_(pref_path
) {}
236 AccessibilityManager::PrefHandler::~PrefHandler() {}
238 void AccessibilityManager::PrefHandler::HandleProfileChanged(
239 Profile
* previous_profile
, Profile
* current_profile
) {
240 // Returns if the current profile is null.
241 if (!current_profile
)
244 // If the user set a pref value on the login screen and is now starting a
245 // session with a new profile, copy the pref value to the profile.
246 if ((previous_profile
&&
247 ProfileHelper::IsSigninProfile(previous_profile
) &&
248 current_profile
->IsNewProfile() &&
249 !ProfileHelper::IsSigninProfile(current_profile
)) ||
250 // Special case for Guest mode:
251 // Guest mode launches a guest-mode browser process before session starts,
252 // so the previous profile is null.
253 (!previous_profile
&&
254 current_profile
->IsGuestSession())) {
255 // Returns if the pref has not been set by the user.
256 const PrefService::Preference
* pref
= ProfileHelper::GetSigninProfile()->
257 GetPrefs()->FindPreference(pref_path_
);
258 if (!pref
|| !pref
->IsUserControlled())
261 // Copy the pref value from the signin screen.
262 const base::Value
* value_on_login
= pref
->GetValue();
263 PrefService
* user_prefs
= current_profile
->GetPrefs();
264 user_prefs
->Set(pref_path_
, *value_on_login
);
268 ///////////////////////////////////////////////////////////////////////////////
270 // AccessibilityManager
273 void AccessibilityManager::Initialize() {
274 CHECK(g_accessibility_manager
== NULL
);
275 g_accessibility_manager
= new AccessibilityManager();
279 void AccessibilityManager::Shutdown() {
280 CHECK(g_accessibility_manager
);
281 delete g_accessibility_manager
;
282 g_accessibility_manager
= NULL
;
286 AccessibilityManager
* AccessibilityManager::Get() {
287 return g_accessibility_manager
;
290 AccessibilityManager::AccessibilityManager()
292 chrome_vox_loaded_on_lock_screen_(false),
293 chrome_vox_loaded_on_user_screen_(false),
294 large_cursor_pref_handler_(prefs::kLargeCursorEnabled
),
295 spoken_feedback_pref_handler_(prefs::kSpokenFeedbackEnabled
),
296 high_contrast_pref_handler_(prefs::kHighContrastEnabled
),
297 autoclick_pref_handler_(prefs::kAutoclickEnabled
),
298 autoclick_delay_pref_handler_(prefs::kAutoclickDelayMs
),
299 large_cursor_enabled_(false),
300 sticky_keys_enabled_(false),
301 spoken_feedback_enabled_(false),
302 high_contrast_enabled_(false),
303 autoclick_enabled_(false),
304 autoclick_delay_ms_(ash::AutoclickController::kDefaultAutoclickDelayMs
),
305 spoken_feedback_notification_(ash::A11Y_NOTIFICATION_NONE
),
306 weak_ptr_factory_(this),
307 should_speak_chrome_vox_announcements_on_user_screen_(true),
308 system_sounds_enabled_(false) {
309 notification_registrar_
.Add(this,
310 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
,
311 content::NotificationService::AllSources());
312 notification_registrar_
.Add(this,
313 chrome::NOTIFICATION_SESSION_STARTED
,
314 content::NotificationService::AllSources());
315 notification_registrar_
.Add(this,
316 chrome::NOTIFICATION_PROFILE_DESTROYED
,
317 content::NotificationService::AllSources());
318 notification_registrar_
.Add(this,
319 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED
,
320 content::NotificationService::AllSources());
321 notification_registrar_
.Add(this,
322 chrome::NOTIFICATION_EXTENSION_REMOVED
,
323 content::NotificationService::AllSources());
324 notification_registrar_
.Add(this,
325 chrome::NOTIFICATION_EXTENSION_UNLOADED
,
326 content::NotificationService::AllSources());
328 GetBrailleController()->AddObserver(this);
330 ui::ResourceBundle
& bundle
= ui::ResourceBundle::GetSharedInstance();
331 media::SoundsManager
* manager
= media::SoundsManager::Get();
332 manager
->Initialize(SOUND_SHUTDOWN
,
333 bundle
.GetRawDataResource(IDR_SOUND_SHUTDOWN_WAV
));
335 SOUND_SPOKEN_FEEDBACK_ENABLED
,
336 bundle
.GetRawDataResource(IDR_SOUND_SPOKEN_FEEDBACK_ENABLED_WAV
));
338 SOUND_SPOKEN_FEEDBACK_DISABLED
,
339 bundle
.GetRawDataResource(IDR_SOUND_SPOKEN_FEEDBACK_DISABLED_WAV
));
342 AccessibilityManager::~AccessibilityManager() {
343 CHECK(this == g_accessibility_manager
);
345 // Component extensions don't always notify us when they're unloaded. Ensure
346 // we clean up ChromeVox observers here.
347 for (std::set
<Profile
*>::iterator it
= chromevox_profiles_
.begin();
348 it
!= chromevox_profiles_
.end();
350 extensions::ExtensionSystem::Get(*it
)->
351 event_router()->UnregisterObserver(this);
355 bool AccessibilityManager::ShouldShowAccessibilityMenu() {
356 // If any of the loaded profiles has an accessibility feature turned on - or
357 // enforced to always show the menu - we return true to show the menu.
358 std::vector
<Profile
*> profiles
=
359 g_browser_process
->profile_manager()->GetLoadedProfiles();
360 for (std::vector
<Profile
*>::iterator it
= profiles
.begin();
361 it
!= profiles
.end();
363 PrefService
* pref_service
= (*it
)->GetPrefs();
364 if (pref_service
->GetBoolean(prefs::kStickyKeysEnabled
) ||
365 pref_service
->GetBoolean(prefs::kLargeCursorEnabled
) ||
366 pref_service
->GetBoolean(prefs::kSpokenFeedbackEnabled
) ||
367 pref_service
->GetBoolean(prefs::kHighContrastEnabled
) ||
368 pref_service
->GetBoolean(prefs::kAutoclickEnabled
) ||
369 pref_service
->GetBoolean(prefs::kShouldAlwaysShowAccessibilityMenu
) ||
370 pref_service
->GetBoolean(prefs::kScreenMagnifierEnabled
))
376 void AccessibilityManager::EnableLargeCursor(bool enabled
) {
380 PrefService
* pref_service
= profile_
->GetPrefs();
381 pref_service
->SetBoolean(prefs::kLargeCursorEnabled
, enabled
);
382 pref_service
->CommitPendingWrite();
385 void AccessibilityManager::UpdateLargeCursorFromPref() {
390 profile_
->GetPrefs()->GetBoolean(prefs::kLargeCursorEnabled
);
392 if (large_cursor_enabled_
== enabled
)
395 large_cursor_enabled_
= enabled
;
397 AccessibilityStatusEventDetails
details(enabled
, ash::A11Y_NOTIFICATION_NONE
);
398 content::NotificationService::current()->Notify(
399 chrome::NOTIFICATION_CROS_ACCESSIBILITY_TOGGLE_LARGE_CURSOR
,
400 content::NotificationService::AllSources(),
401 content::Details
<AccessibilityStatusEventDetails
>(&details
));
404 // Large cursor is implemented only in ash.
405 ash::Shell::GetInstance()->cursor_manager()->SetCursorSet(
406 enabled
? ui::CURSOR_SET_LARGE
: ui::CURSOR_SET_NORMAL
);
410 bool AccessibilityManager::IsIncognitoAllowed() {
411 UserManager
* user_manager
= UserManager::Get();
412 // Supervised users can't create incognito-mode windows.
413 return !(user_manager
->IsLoggedInAsLocallyManagedUser());
416 bool AccessibilityManager::IsLargeCursorEnabled() {
417 return large_cursor_enabled_
;
420 void AccessibilityManager::EnableStickyKeys(bool enabled
) {
423 PrefService
* pref_service
= profile_
->GetPrefs();
424 pref_service
->SetBoolean(prefs::kStickyKeysEnabled
, enabled
);
425 pref_service
->CommitPendingWrite();
428 bool AccessibilityManager::IsStickyKeysEnabled() {
429 return sticky_keys_enabled_
;
432 void AccessibilityManager::UpdateStickyKeysFromPref() {
437 profile_
->GetPrefs()->GetBoolean(prefs::kStickyKeysEnabled
);
439 if (sticky_keys_enabled_
== enabled
)
442 sticky_keys_enabled_
= enabled
;
444 // Sticky keys is implemented only in ash.
445 ash::Shell::GetInstance()->sticky_keys_controller()->Enable(enabled
);
449 void AccessibilityManager::EnableSpokenFeedback(
451 ash::AccessibilityNotificationVisibility notify
) {
455 ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(
456 enabled
? ash::UMA_STATUS_AREA_ENABLE_SPOKEN_FEEDBACK
457 : ash::UMA_STATUS_AREA_DISABLE_SPOKEN_FEEDBACK
);
459 spoken_feedback_notification_
= notify
;
461 PrefService
* pref_service
= profile_
->GetPrefs();
462 pref_service
->SetBoolean(
463 prefs::kSpokenFeedbackEnabled
, enabled
);
464 pref_service
->CommitPendingWrite();
466 spoken_feedback_notification_
= ash::A11Y_NOTIFICATION_NONE
;
469 void AccessibilityManager::UpdateSpokenFeedbackFromPref() {
474 profile_
->GetPrefs()->GetBoolean(prefs::kSpokenFeedbackEnabled
);
476 if (spoken_feedback_enabled_
== enabled
)
479 spoken_feedback_enabled_
= enabled
;
481 ExtensionAccessibilityEventRouter::GetInstance()->
482 SetAccessibilityEnabled(enabled
);
484 AccessibilityStatusEventDetails
details(enabled
,
485 spoken_feedback_notification_
);
486 content::NotificationService::current()->Notify(
487 chrome::NOTIFICATION_CROS_ACCESSIBILITY_TOGGLE_SPOKEN_FEEDBACK
,
488 content::NotificationService::AllSources(),
489 content::Details
<AccessibilityStatusEventDetails
>(&details
));
494 ExtensionAccessibilityEventRouter::GetInstance()->
495 OnChromeVoxLoadStateChanged(profile_
, false, false);
499 void AccessibilityManager::LoadChromeVox() {
500 SetUpPreLoadChromeVox(profile_
);
502 ScreenLocker
* screen_locker
= ScreenLocker::default_screen_locker();
503 if (screen_locker
&& screen_locker
->locked()) {
504 // If on the lock screen, loads ChromeVox only to the lock screen as for
505 // now. On unlock, it will be loaded to the user screen.
506 // (see. AccessibilityManager::Observe())
507 LoadChromeVoxToLockScreen();
511 LoadChromeVoxToUserScreen();
514 void AccessibilityManager::LoadChromeVoxToUserScreen() {
515 if (chrome_vox_loaded_on_user_screen_
)
518 // Determine whether an OOBE screen is currently being shown. If so,
519 // ChromeVox will be injected directly into that screen.
520 content::WebUI
* login_web_ui
= NULL
;
522 if (ProfileHelper::IsSigninProfile(profile_
)) {
523 LoginDisplayHost
* login_display_host
= LoginDisplayHostImpl::default_host();
524 if (login_display_host
) {
525 WebUILoginView
* web_ui_login_view
=
526 login_display_host
->GetWebUILoginView();
527 if (web_ui_login_view
)
528 login_web_ui
= web_ui_login_view
->GetWebUI();
532 LoadChromeVoxExtension(profile_
, login_web_ui
);
533 chrome_vox_loaded_on_user_screen_
= true;
536 void AccessibilityManager::LoadChromeVoxToLockScreen() {
537 if (chrome_vox_loaded_on_lock_screen_
)
540 ScreenLocker
* screen_locker
= ScreenLocker::default_screen_locker();
541 if (screen_locker
&& screen_locker
->locked()) {
542 content::WebUI
* lock_web_ui
= screen_locker
->GetAssociatedWebUI();
544 Profile
* profile
= Profile::FromWebUI(lock_web_ui
);
545 LoadChromeVoxExtension(profile
, lock_web_ui
);
546 chrome_vox_loaded_on_lock_screen_
= true;
551 void AccessibilityManager::UnloadChromeVox() {
552 if (system_sounds_enabled_
) {
553 ash::PlaySystemSound(SOUND_SPOKEN_FEEDBACK_DISABLED
,
554 false /* honor_spoken_feedback */);
556 if (chrome_vox_loaded_on_lock_screen_
)
557 UnloadChromeVoxFromLockScreen();
559 if (chrome_vox_loaded_on_user_screen_
) {
560 UnloadChromeVoxExtension(profile_
);
561 chrome_vox_loaded_on_user_screen_
= false;
565 void AccessibilityManager::UnloadChromeVoxFromLockScreen() {
566 // Lock screen uses the signin progile.
567 Profile
* signin_profile
= ProfileHelper::GetSigninProfile();
568 UnloadChromeVoxExtension(signin_profile
);
569 chrome_vox_loaded_on_lock_screen_
= false;
572 bool AccessibilityManager::IsSpokenFeedbackEnabled() {
573 return spoken_feedback_enabled_
;
576 void AccessibilityManager::ToggleSpokenFeedback(
577 ash::AccessibilityNotificationVisibility notify
) {
578 EnableSpokenFeedback(!IsSpokenFeedbackEnabled(), notify
);
581 void AccessibilityManager::EnableHighContrast(bool enabled
) {
585 PrefService
* pref_service
= profile_
->GetPrefs();
586 pref_service
->SetBoolean(prefs::kHighContrastEnabled
, enabled
);
587 pref_service
->CommitPendingWrite();
590 void AccessibilityManager::UpdateHighContrastFromPref() {
595 profile_
->GetPrefs()->GetBoolean(prefs::kHighContrastEnabled
);
597 if (high_contrast_enabled_
== enabled
)
600 high_contrast_enabled_
= enabled
;
602 AccessibilityStatusEventDetails
detail(enabled
, ash::A11Y_NOTIFICATION_NONE
);
603 content::NotificationService::current()->Notify(
604 chrome::NOTIFICATION_CROS_ACCESSIBILITY_TOGGLE_HIGH_CONTRAST_MODE
,
605 content::NotificationService::AllSources(),
606 content::Details
<AccessibilityStatusEventDetails
>(&detail
));
609 ash::Shell::GetInstance()->high_contrast_controller()->SetEnabled(enabled
);
613 void AccessibilityManager::LocalePrefChanged() {
617 if (!IsSpokenFeedbackEnabled())
620 // If the system locale changes and spoken feedback is enabled,
621 // reload ChromeVox so that it switches its internal translations
622 // to the new language.
623 EnableSpokenFeedback(false, ash::A11Y_NOTIFICATION_NONE
);
624 EnableSpokenFeedback(true, ash::A11Y_NOTIFICATION_NONE
);
627 bool AccessibilityManager::IsHighContrastEnabled() {
628 return high_contrast_enabled_
;
631 void AccessibilityManager::EnableAutoclick(bool enabled
) {
635 PrefService
* pref_service
= profile_
->GetPrefs();
636 pref_service
->SetBoolean(prefs::kAutoclickEnabled
, enabled
);
637 pref_service
->CommitPendingWrite();
640 bool AccessibilityManager::IsAutoclickEnabled() {
641 return autoclick_enabled_
;
644 void AccessibilityManager::UpdateAutoclickFromPref() {
646 profile_
->GetPrefs()->GetBoolean(prefs::kAutoclickEnabled
);
648 if (autoclick_enabled_
== enabled
)
650 autoclick_enabled_
= enabled
;
653 ash::Shell::GetInstance()->autoclick_controller()->SetEnabled(enabled
);
657 void AccessibilityManager::SetAutoclickDelay(int delay_ms
) {
661 PrefService
* pref_service
= profile_
->GetPrefs();
662 pref_service
->SetInteger(prefs::kAutoclickDelayMs
, delay_ms
);
663 pref_service
->CommitPendingWrite();
666 int AccessibilityManager::GetAutoclickDelay() const {
667 return autoclick_delay_ms_
;
670 void AccessibilityManager::UpdateAutoclickDelayFromPref() {
671 int autoclick_delay_ms
=
672 profile_
->GetPrefs()->GetInteger(prefs::kAutoclickDelayMs
);
674 if (autoclick_delay_ms
== autoclick_delay_ms_
)
676 autoclick_delay_ms_
= autoclick_delay_ms
;
679 ash::Shell::GetInstance()->autoclick_controller()->SetAutoclickDelay(
680 autoclick_delay_ms_
);
684 void AccessibilityManager::CheckBrailleState() {
685 BrowserThread::PostTaskAndReplyWithResult(
686 BrowserThread::IO
, FROM_HERE
, base::Bind(
687 &BrailleController::GetDisplayState
,
688 base::Unretained(GetBrailleController())),
689 base::Bind(&AccessibilityManager::ReceiveBrailleDisplayState
,
690 weak_ptr_factory_
.GetWeakPtr()));
693 void AccessibilityManager::ReceiveBrailleDisplayState(
694 scoped_ptr
<extensions::api::braille_display_private::DisplayState
> state
) {
695 OnDisplayStateChanged(*state
);
699 void AccessibilityManager::SetProfile(Profile
* profile
) {
700 pref_change_registrar_
.reset();
701 local_state_pref_change_registrar_
.reset();
704 // TODO(yoshiki): Move following code to PrefHandler.
705 pref_change_registrar_
.reset(new PrefChangeRegistrar
);
706 pref_change_registrar_
->Init(profile
->GetPrefs());
707 pref_change_registrar_
->Add(
708 prefs::kLargeCursorEnabled
,
709 base::Bind(&AccessibilityManager::UpdateLargeCursorFromPref
,
710 base::Unretained(this)));
711 pref_change_registrar_
->Add(
712 prefs::kStickyKeysEnabled
,
713 base::Bind(&AccessibilityManager::UpdateStickyKeysFromPref
,
714 base::Unretained(this)));
715 pref_change_registrar_
->Add(
716 prefs::kSpokenFeedbackEnabled
,
717 base::Bind(&AccessibilityManager::UpdateSpokenFeedbackFromPref
,
718 base::Unretained(this)));
719 pref_change_registrar_
->Add(
720 prefs::kHighContrastEnabled
,
721 base::Bind(&AccessibilityManager::UpdateHighContrastFromPref
,
722 base::Unretained(this)));
723 pref_change_registrar_
->Add(
724 prefs::kAutoclickEnabled
,
725 base::Bind(&AccessibilityManager::UpdateAutoclickFromPref
,
726 base::Unretained(this)));
727 pref_change_registrar_
->Add(
728 prefs::kAutoclickDelayMs
,
729 base::Bind(&AccessibilityManager::UpdateAutoclickDelayFromPref
,
730 base::Unretained(this)));
732 local_state_pref_change_registrar_
.reset(new PrefChangeRegistrar
);
733 local_state_pref_change_registrar_
->Init(g_browser_process
->local_state());
734 local_state_pref_change_registrar_
->Add(
735 prefs::kApplicationLocale
,
736 base::Bind(&AccessibilityManager::LocalePrefChanged
,
737 base::Unretained(this)));
739 content::BrowserAccessibilityState::GetInstance()->AddHistogramCallback(
741 &AccessibilityManager::UpdateChromeOSAccessibilityHistograms
,
742 base::Unretained(this)));
745 large_cursor_pref_handler_
.HandleProfileChanged(profile_
, profile
);
746 spoken_feedback_pref_handler_
.HandleProfileChanged(profile_
, profile
);
747 high_contrast_pref_handler_
.HandleProfileChanged(profile_
, profile
);
748 autoclick_pref_handler_
.HandleProfileChanged(profile_
, profile
);
749 autoclick_delay_pref_handler_
.HandleProfileChanged(profile_
, profile
);
751 if (profile
&& spoken_feedback_enabled_
)
752 SetUpPreLoadChromeVox(profile
);
754 if (!profile_
&& profile
)
758 UpdateLargeCursorFromPref();
759 UpdateStickyKeysFromPref();
760 UpdateSpokenFeedbackFromPref();
761 UpdateHighContrastFromPref();
762 UpdateAutoclickFromPref();
763 UpdateAutoclickDelayFromPref();
766 void AccessibilityManager::ActiveUserChanged(const std::string
& user_id
) {
767 SetProfile(ProfileManager::GetActiveUserProfile());
770 void AccessibilityManager::SetProfileForTest(Profile
* profile
) {
774 void AccessibilityManager::SetBrailleControllerForTest(
775 BrailleController
* controller
) {
776 g_braille_controller_for_test
= controller
;
779 void AccessibilityManager::EnableSystemSounds(bool system_sounds_enabled
) {
780 system_sounds_enabled_
= system_sounds_enabled
;
783 base::TimeDelta
AccessibilityManager::PlayShutdownSound() {
784 if (!system_sounds_enabled_
)
785 return base::TimeDelta();
786 system_sounds_enabled_
= false;
787 if (!ash::PlaySystemSound(SOUND_SHUTDOWN
, true /* honor_spoken_feedback */))
788 return base::TimeDelta();
789 return media::SoundsManager::Get()->GetDuration(SOUND_SHUTDOWN
);
792 void AccessibilityManager::UpdateChromeOSAccessibilityHistograms() {
793 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosSpokenFeedback",
794 IsSpokenFeedbackEnabled());
795 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosHighContrast",
796 IsHighContrastEnabled());
797 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosVirtualKeyboard",
798 accessibility::IsVirtualKeyboardEnabled());
799 if (MagnificationManager::Get()) {
800 uint32 type
= MagnificationManager::Get()->IsMagnifierEnabled() ?
801 MagnificationManager::Get()->GetMagnifierType() : 0;
802 // '0' means magnifier is disabled.
803 UMA_HISTOGRAM_ENUMERATION("Accessibility.CrosScreenMagnifier",
805 ash::kMaxMagnifierType
+ 1);
808 const PrefService
* const prefs
= profile_
->GetPrefs();
809 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosLargeCursor",
810 prefs
->GetBoolean(prefs::kLargeCursorEnabled
));
811 UMA_HISTOGRAM_BOOLEAN(
812 "Accessibility.CrosAlwaysShowA11yMenu",
813 prefs
->GetBoolean(prefs::kShouldAlwaysShowAccessibilityMenu
));
815 bool autoclick_enabled
= prefs
->GetBoolean(prefs::kAutoclickEnabled
);
816 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosAutoclick", autoclick_enabled
);
817 if (autoclick_enabled
) {
818 // We only want to log the autoclick delay if the user has actually
819 // enabled autoclick.
820 UMA_HISTOGRAM_CUSTOM_TIMES(
821 "Accessibility.CrosAutoclickDelay",
822 base::TimeDelta::FromMilliseconds(
823 prefs
->GetInteger(prefs::kAutoclickDelayMs
)),
824 base::TimeDelta::FromMilliseconds(1),
825 base::TimeDelta::FromMilliseconds(3000),
831 void AccessibilityManager::Observe(
833 const content::NotificationSource
& source
,
834 const content::NotificationDetails
& details
) {
836 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
: {
837 // Update |profile_| when entering the login screen.
838 Profile
* profile
= ProfileManager::GetActiveUserProfile();
839 if (ProfileHelper::IsSigninProfile(profile
))
843 case chrome::NOTIFICATION_SESSION_STARTED
:
844 // Update |profile_| when entering a session.
845 SetProfile(ProfileManager::GetActiveUserProfile());
847 // Ensure ChromeVox makes announcements at the start of new sessions.
848 should_speak_chrome_vox_announcements_on_user_screen_
= true;
850 // Add a session state observer to be able to monitor session changes.
851 if (!session_state_observer_
.get() && ash::Shell::HasInstance())
852 session_state_observer_
.reset(
853 new ash::ScopedSessionStateObserver(this));
855 case chrome::NOTIFICATION_PROFILE_DESTROYED
: {
856 // Update |profile_| when exiting a session or shutting down.
857 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
858 if (profile_
== profile
)
861 if (IsSpokenFeedbackEnabled())
862 TearDownPostUnloadChromeVox(profile
);
865 case chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED
: {
866 bool is_screen_locked
= *content::Details
<bool>(details
).ptr();
867 if (spoken_feedback_enabled_
) {
868 if (is_screen_locked
) {
869 LoadChromeVoxToLockScreen();
871 // Status tray gets verbalized by user screen ChromeVox, so we need
873 LoadChromeVoxToUserScreen();
875 // Lock screen destroys its resources; no need for us to explicitly
877 chrome_vox_loaded_on_lock_screen_
= false;
879 // However, if spoken feedback was enabled, also enable it on the user
881 LoadChromeVoxToUserScreen();
886 case chrome::NOTIFICATION_EXTENSION_UNLOADED
: {
887 extensions::UnloadedExtensionInfo
* info
=
888 content::Details
<extensions::UnloadedExtensionInfo
>(details
).ptr();
889 const extensions::Extension
* extension
= info
->extension
;
890 if (extension
->id() == extension_misc::kChromeVoxExtensionId
) {
891 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
892 TearDownPostUnloadChromeVox(profile
);
896 case chrome::NOTIFICATION_EXTENSION_REMOVED
: {
897 const extensions::Extension
* extension
=
898 content::Details
<const extensions::Extension
>(details
).ptr();
899 if (extension
->id() == extension_misc::kChromeVoxExtensionId
) {
900 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
901 TearDownPostUnloadChromeVox(profile
);
908 void AccessibilityManager::OnDisplayStateChanged(
909 const DisplayState
& display_state
) {
910 if (display_state
.available
)
911 EnableSpokenFeedback(true, ash::A11Y_NOTIFICATION_SHOW
);
914 void AccessibilityManager::OnListenerAdded(
915 const extensions::EventListenerInfo
& details
) {
916 if (details
.extension_id
!= extension_misc::kChromeVoxExtensionId
)
919 ExtensionAccessibilityEventRouter::GetInstance()->
920 OnChromeVoxLoadStateChanged(profile_
,
921 IsSpokenFeedbackEnabled(),
922 chrome_vox_loaded_on_lock_screen_
||
923 should_speak_chrome_vox_announcements_on_user_screen_
);
925 should_speak_chrome_vox_announcements_on_user_screen_
=
926 chrome_vox_loaded_on_lock_screen_
;
929 void AccessibilityManager::OnListenerRemoved(
930 const extensions::EventListenerInfo
& details
) {
931 if (details
.extension_id
!= extension_misc::kChromeVoxExtensionId
)
936 // It's possible for a user to rapidly toggle ChromeVox on/off state. Load
937 // ChromeVox again if we've been enabled while disabling.
938 if (IsSpokenFeedbackEnabled())
942 void AccessibilityManager::SetUpPreLoadChromeVox(Profile
* profile
) {
943 // Do any setup work needed immediately before ChromeVox actually loads.
944 if (system_sounds_enabled_
) {
945 ash::PlaySystemSound(SOUND_SPOKEN_FEEDBACK_ENABLED
,
946 false /* honor_spoken_feedback */);
950 extensions::ExtensionSystem::Get(profile
)->
951 event_router()->RegisterObserver(this,
952 extensions::api::experimental_accessibility::
953 OnChromeVoxLoadStateChanged::kEventName
);
954 chromevox_profiles_
.insert(profile
);
958 void AccessibilityManager::TearDownPostUnloadChromeVox(Profile
* profile
) {
959 // Do any teardown work needed immediately after ChromeVox actually unloads.
961 extensions::ExtensionSystem::Get(profile
)->
962 event_router()->UnregisterObserver(this);
963 chromevox_profiles_
.erase(profile
);
967 } // namespace chromeos