Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / chromeos / accessibility / accessibility_manager.cc
blobb4eda1e087213c32834386a13d4ce4f1a81a326c
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/session_state_delegate.h"
12 #include "ash/shell.h"
13 #include "ash/sticky_keys/sticky_keys_controller.h"
14 #include "ash/system/tray/system_tray_notifier.h"
15 #include "base/callback.h"
16 #include "base/callback_helpers.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/memory/singleton.h"
19 #include "base/metrics/histogram.h"
20 #include "base/path_service.h"
21 #include "base/prefs/pref_member.h"
22 #include "base/prefs/pref_service.h"
23 #include "base/strings/string_split.h"
24 #include "base/strings/string_util.h"
25 #include "base/time/time.h"
26 #include "base/values.h"
27 #include "chrome/browser/accessibility/accessibility_extension_api.h"
28 #include "chrome/browser/browser_process.h"
29 #include "chrome/browser/chrome_notification_types.h"
30 #include "chrome/browser/chromeos/accessibility/magnification_manager.h"
31 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
32 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
33 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
34 #include "chrome/browser/chromeos/login/ui/webui_login_view.h"
35 #include "chrome/browser/chromeos/profiles/profile_helper.h"
36 #include "chrome/browser/chromeos/ui/accessibility_focus_ring_controller.h"
37 #include "chrome/browser/extensions/component_loader.h"
38 #include "chrome/browser/extensions/extension_service.h"
39 #include "chrome/browser/extensions/tab_helper.h"
40 #include "chrome/browser/prefs/incognito_mode_prefs.h"
41 #include "chrome/browser/profiles/profile.h"
42 #include "chrome/browser/profiles/profile_manager.h"
43 #include "chrome/common/chrome_paths.h"
44 #include "chrome/common/extensions/api/accessibility_private.h"
45 #include "chrome/common/extensions/extension_constants.h"
46 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
47 #include "chrome/common/pref_names.h"
48 #include "chrome/grit/browser_resources.h"
49 #include "chromeos/audio/chromeos_sounds.h"
50 #include "chromeos/login/login_state.h"
51 #include "components/user_manager/user_manager.h"
52 #include "content/public/browser/browser_accessibility_state.h"
53 #include "content/public/browser/browser_thread.h"
54 #include "content/public/browser/notification_details.h"
55 #include "content/public/browser/notification_service.h"
56 #include "content/public/browser/notification_source.h"
57 #include "content/public/browser/render_process_host.h"
58 #include "content/public/browser/render_view_host.h"
59 #include "content/public/browser/web_contents.h"
60 #include "content/public/browser/web_ui.h"
61 #include "extensions/browser/event_router.h"
62 #include "extensions/browser/extension_registry.h"
63 #include "extensions/browser/extension_system.h"
64 #include "extensions/browser/file_reader.h"
65 #include "extensions/browser/script_executor.h"
66 #include "extensions/common/extension.h"
67 #include "extensions/common/extension_messages.h"
68 #include "extensions/common/extension_resource.h"
69 #include "extensions/common/host_id.h"
70 #include "media/audio/sounds/sounds_manager.h"
71 #include "ui/base/ime/chromeos/input_method_manager.h"
72 #include "ui/base/resource/resource_bundle.h"
73 #include "ui/keyboard/keyboard_controller.h"
74 #include "ui/keyboard/keyboard_util.h"
76 using content::BrowserThread;
77 using content::RenderViewHost;
78 using extensions::api::braille_display_private::BrailleController;
79 using extensions::api::braille_display_private::DisplayState;
80 using extensions::api::braille_display_private::KeyEvent;
82 namespace chromeos {
84 namespace {
86 static chromeos::AccessibilityManager* g_accessibility_manager = NULL;
88 static BrailleController* g_braille_controller_for_test = NULL;
90 BrailleController* GetBrailleController() {
91 return g_braille_controller_for_test
92 ? g_braille_controller_for_test
93 : BrailleController::GetInstance();
96 base::FilePath GetChromeVoxPath() {
97 base::FilePath path;
98 if (!PathService::Get(chrome::DIR_RESOURCES, &path))
99 NOTREACHED();
100 path = path.Append(extension_misc::kChromeVoxExtensionPath);
101 return path;
104 // Uses the ScriptExecutor associated with the given |render_view_host| to
105 // execute the given |code|.
106 void ExecuteScriptHelper(
107 content::RenderViewHost* render_view_host,
108 const std::string& code,
109 const std::string& extension_id) {
110 content::WebContents* web_contents =
111 content::WebContents::FromRenderViewHost(render_view_host);
112 if (!web_contents)
113 return;
114 if (!extensions::TabHelper::FromWebContents(web_contents))
115 extensions::TabHelper::CreateForWebContents(web_contents);
116 extensions::TabHelper::FromWebContents(web_contents)->script_executor()->
117 ExecuteScript(HostID(HostID::EXTENSIONS, extension_id),
118 extensions::ScriptExecutor::JAVASCRIPT,
119 code,
120 extensions::ScriptExecutor::ALL_FRAMES,
121 extensions::ScriptExecutor::DONT_MATCH_ABOUT_BLANK,
122 extensions::UserScript::DOCUMENT_IDLE,
123 extensions::ScriptExecutor::ISOLATED_WORLD,
124 extensions::ScriptExecutor::DEFAULT_PROCESS,
125 GURL(), // No webview src.
126 GURL(), // No file url.
127 false, // Not user gesture.
128 extensions::ScriptExecutor::NO_RESULT,
129 extensions::ScriptExecutor::ExecuteScriptCallback());
132 // Helper class that directly loads an extension's content scripts into
133 // all of the frames corresponding to a given RenderViewHost.
134 class ContentScriptLoader {
135 public:
136 // Initialize the ContentScriptLoader with the ID of the extension
137 // and the RenderViewHost where the scripts should be loaded.
138 ContentScriptLoader(const std::string& extension_id,
139 int render_process_id,
140 int render_view_id)
141 : extension_id_(extension_id),
142 render_process_id_(render_process_id),
143 render_view_id_(render_view_id) {}
145 // Call this once with the ExtensionResource corresponding to each
146 // content script to be loaded.
147 void AppendScript(extensions::ExtensionResource resource) {
148 resources_.push(resource);
151 // Finally, call this method once to fetch all of the resources and
152 // load them. This method will delete this object when done.
153 void Run() {
154 if (resources_.empty()) {
155 delete this;
156 return;
159 extensions::ExtensionResource resource = resources_.front();
160 resources_.pop();
161 scoped_refptr<FileReader> reader(new FileReader(resource, base::Bind(
162 &ContentScriptLoader::OnFileLoaded, base::Unretained(this))));
163 reader->Start();
166 private:
167 void OnFileLoaded(bool success, const std::string& data) {
168 if (success) {
169 RenderViewHost* render_view_host =
170 RenderViewHost::FromID(render_process_id_, render_view_id_);
171 if (render_view_host)
172 ExecuteScriptHelper(render_view_host, data, extension_id_);
174 Run();
177 std::string extension_id_;
178 int render_process_id_;
179 int render_view_id_;
180 std::queue<extensions::ExtensionResource> resources_;
183 void InjectChromeVoxContentScript(
184 ExtensionService* extension_service,
185 int render_process_id,
186 int render_view_id,
187 const base::Closure& done_cb);
189 void LoadChromeVoxExtension(
190 Profile* profile,
191 RenderViewHost* render_view_host,
192 base::Closure done_cb) {
193 ExtensionService* extension_service =
194 extensions::ExtensionSystem::Get(profile)->extension_service();
195 if (render_view_host) {
196 // Wrap the passed in callback to inject the content script.
197 done_cb = base::Bind(
198 &InjectChromeVoxContentScript,
199 extension_service,
200 render_view_host->GetProcess()->GetID(),
201 render_view_host->GetRoutingID(),
202 done_cb);
204 extension_service->component_loader()->AddChromeVoxExtension(done_cb);
207 void InjectChromeVoxContentScript(
208 ExtensionService* extension_service,
209 int render_process_id,
210 int render_view_id,
211 const base::Closure& done_cb) {
212 // Make sure to always run |done_cb|. ChromeVox was loaded even if we end up
213 // not injecting into this particular render view.
214 base::ScopedClosureRunner done_runner(done_cb);
215 RenderViewHost* render_view_host =
216 RenderViewHost::FromID(render_process_id, render_view_id);
217 if (!render_view_host)
218 return;
219 const extensions::Extension* extension =
220 extensions::ExtensionRegistry::Get(extension_service->profile())
221 ->enabled_extensions()
222 .GetByID(extension_misc::kChromeVoxExtensionId);
224 // Set a flag to tell ChromeVox that it's just been enabled,
225 // so that it won't interrupt our speech feedback enabled message.
226 ExecuteScriptHelper(render_view_host, "window.INJECTED_AFTER_LOAD = true;",
227 extension->id());
229 // Inject ChromeVox' content scripts.
230 ContentScriptLoader* loader = new ContentScriptLoader(
231 extension->id(), render_view_host->GetProcess()->GetID(),
232 render_view_host->GetRoutingID());
234 const extensions::UserScriptList& content_scripts =
235 extensions::ContentScriptsInfo::GetContentScripts(extension);
236 for (size_t i = 0; i < content_scripts.size(); i++) {
237 const extensions::UserScript& script = content_scripts[i];
238 for (size_t j = 0; j < script.js_scripts().size(); ++j) {
239 const extensions::UserScript::File& file = script.js_scripts()[j];
240 extensions::ExtensionResource resource = extension->GetResource(
241 file.relative_path());
242 loader->AppendScript(resource);
245 loader->Run(); // It cleans itself up when done.
248 void UnloadChromeVoxExtension(Profile* profile) {
249 base::FilePath path = GetChromeVoxPath();
250 ExtensionService* extension_service =
251 extensions::ExtensionSystem::Get(profile)->extension_service();
252 extension_service->component_loader()->Remove(path);
255 } // namespace
257 ///////////////////////////////////////////////////////////////////////////////
258 // AccessibilityStatusEventDetails
260 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
261 AccessibilityNotificationType notification_type,
262 bool enabled,
263 ui::AccessibilityNotificationVisibility notify)
264 : notification_type(notification_type),
265 enabled(enabled),
266 magnifier_type(ui::kDefaultMagnifierType),
267 notify(notify) {}
269 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
270 AccessibilityNotificationType notification_type,
271 bool enabled,
272 ui::MagnifierType magnifier_type,
273 ui::AccessibilityNotificationVisibility notify)
274 : notification_type(notification_type),
275 enabled(enabled),
276 magnifier_type(magnifier_type),
277 notify(notify) {}
279 ///////////////////////////////////////////////////////////////////////////////
281 // AccessibilityManager::PrefHandler
283 AccessibilityManager::PrefHandler::PrefHandler(const char* pref_path)
284 : pref_path_(pref_path) {}
286 AccessibilityManager::PrefHandler::~PrefHandler() {}
288 void AccessibilityManager::PrefHandler::HandleProfileChanged(
289 Profile* previous_profile, Profile* current_profile) {
290 // Returns if the current profile is null.
291 if (!current_profile)
292 return;
294 // If the user set a pref value on the login screen and is now starting a
295 // session with a new profile, copy the pref value to the profile.
296 if ((previous_profile &&
297 ProfileHelper::IsSigninProfile(previous_profile) &&
298 current_profile->IsNewProfile() &&
299 !ProfileHelper::IsSigninProfile(current_profile)) ||
300 // Special case for Guest mode:
301 // Guest mode launches a guest-mode browser process before session starts,
302 // so the previous profile is null.
303 (!previous_profile &&
304 current_profile->IsGuestSession())) {
305 // Returns if the pref has not been set by the user.
306 const PrefService::Preference* pref = ProfileHelper::GetSigninProfile()->
307 GetPrefs()->FindPreference(pref_path_);
308 if (!pref || !pref->IsUserControlled())
309 return;
311 // Copy the pref value from the signin screen.
312 const base::Value* value_on_login = pref->GetValue();
313 PrefService* user_prefs = current_profile->GetPrefs();
314 user_prefs->Set(pref_path_, *value_on_login);
318 ///////////////////////////////////////////////////////////////////////////////
320 // AccessibilityManager
322 // static
323 void AccessibilityManager::Initialize() {
324 CHECK(g_accessibility_manager == NULL);
325 g_accessibility_manager = new AccessibilityManager();
328 // static
329 void AccessibilityManager::Shutdown() {
330 CHECK(g_accessibility_manager);
331 delete g_accessibility_manager;
332 g_accessibility_manager = NULL;
335 // static
336 AccessibilityManager* AccessibilityManager::Get() {
337 return g_accessibility_manager;
340 AccessibilityManager::AccessibilityManager()
341 : profile_(NULL),
342 chrome_vox_loaded_on_lock_screen_(false),
343 chrome_vox_loaded_on_user_screen_(false),
344 large_cursor_pref_handler_(prefs::kAccessibilityLargeCursorEnabled),
345 spoken_feedback_pref_handler_(prefs::kAccessibilitySpokenFeedbackEnabled),
346 high_contrast_pref_handler_(prefs::kAccessibilityHighContrastEnabled),
347 autoclick_pref_handler_(prefs::kAccessibilityAutoclickEnabled),
348 autoclick_delay_pref_handler_(prefs::kAccessibilityAutoclickDelayMs),
349 virtual_keyboard_pref_handler_(
350 prefs::kAccessibilityVirtualKeyboardEnabled),
351 large_cursor_enabled_(false),
352 sticky_keys_enabled_(false),
353 spoken_feedback_enabled_(false),
354 high_contrast_enabled_(false),
355 autoclick_enabled_(false),
356 autoclick_delay_ms_(ash::AutoclickController::kDefaultAutoclickDelayMs),
357 virtual_keyboard_enabled_(false),
358 spoken_feedback_notification_(ui::A11Y_NOTIFICATION_NONE),
359 should_speak_chrome_vox_announcements_on_user_screen_(true),
360 system_sounds_enabled_(false),
361 braille_display_connected_(false),
362 scoped_braille_observer_(this),
363 braille_ime_current_(false),
364 weak_ptr_factory_(this) {
365 notification_registrar_.Add(this,
366 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
367 content::NotificationService::AllSources());
368 notification_registrar_.Add(this,
369 chrome::NOTIFICATION_SESSION_STARTED,
370 content::NotificationService::AllSources());
371 notification_registrar_.Add(this,
372 chrome::NOTIFICATION_PROFILE_DESTROYED,
373 content::NotificationService::AllSources());
374 notification_registrar_.Add(this,
375 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED,
376 content::NotificationService::AllSources());
378 input_method::InputMethodManager::Get()->AddObserver(this);
380 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
381 media::SoundsManager* manager = media::SoundsManager::Get();
382 manager->Initialize(SOUND_SHUTDOWN,
383 bundle.GetRawDataResource(IDR_SOUND_SHUTDOWN_WAV));
384 manager->Initialize(
385 SOUND_SPOKEN_FEEDBACK_ENABLED,
386 bundle.GetRawDataResource(IDR_SOUND_SPOKEN_FEEDBACK_ENABLED_WAV));
387 manager->Initialize(
388 SOUND_SPOKEN_FEEDBACK_DISABLED,
389 bundle.GetRawDataResource(IDR_SOUND_SPOKEN_FEEDBACK_DISABLED_WAV));
390 manager->Initialize(SOUND_PASSTHROUGH,
391 bundle.GetRawDataResource(IDR_SOUND_PASSTHROUGH_WAV));
392 manager->Initialize(SOUND_EXIT_SCREEN,
393 bundle.GetRawDataResource(IDR_SOUND_EXIT_SCREEN_WAV));
394 manager->Initialize(SOUND_ENTER_SCREEN,
395 bundle.GetRawDataResource(IDR_SOUND_ENTER_SCREEN_WAV));
398 AccessibilityManager::~AccessibilityManager() {
399 CHECK(this == g_accessibility_manager);
400 AccessibilityStatusEventDetails details(
401 ACCESSIBILITY_MANAGER_SHUTDOWN,
402 false,
403 ui::A11Y_NOTIFICATION_NONE);
404 NotifyAccessibilityStatusChanged(details);
405 input_method::InputMethodManager::Get()->RemoveObserver(this);
408 bool AccessibilityManager::ShouldShowAccessibilityMenu() {
409 // If any of the loaded profiles has an accessibility feature turned on - or
410 // enforced to always show the menu - we return true to show the menu.
411 std::vector<Profile*> profiles =
412 g_browser_process->profile_manager()->GetLoadedProfiles();
413 for (std::vector<Profile*>::iterator it = profiles.begin();
414 it != profiles.end();
415 ++it) {
416 PrefService* pref_service = (*it)->GetPrefs();
417 if (pref_service->GetBoolean(prefs::kAccessibilityStickyKeysEnabled) ||
418 pref_service->GetBoolean(prefs::kAccessibilityLargeCursorEnabled) ||
419 pref_service->GetBoolean(prefs::kAccessibilitySpokenFeedbackEnabled) ||
420 pref_service->GetBoolean(prefs::kAccessibilityHighContrastEnabled) ||
421 pref_service->GetBoolean(prefs::kAccessibilityAutoclickEnabled) ||
422 pref_service->GetBoolean(prefs::kShouldAlwaysShowAccessibilityMenu) ||
423 pref_service->GetBoolean(prefs::kAccessibilityScreenMagnifierEnabled) ||
424 pref_service->GetBoolean(prefs::kAccessibilityVirtualKeyboardEnabled))
425 return true;
427 return false;
430 bool AccessibilityManager::ShouldEnableCursorCompositing() {
431 if (!profile_)
432 return false;
433 PrefService* pref_service = profile_->GetPrefs();
434 // Enable cursor compositing when one or more of the listed accessibility
435 // features are turned on.
436 if (pref_service->GetBoolean(prefs::kAccessibilityLargeCursorEnabled) ||
437 pref_service->GetBoolean(prefs::kAccessibilityHighContrastEnabled) ||
438 pref_service->GetBoolean(prefs::kAccessibilityScreenMagnifierEnabled))
439 return true;
440 return false;
443 void AccessibilityManager::EnableLargeCursor(bool enabled) {
444 if (!profile_)
445 return;
447 PrefService* pref_service = profile_->GetPrefs();
448 pref_service->SetBoolean(prefs::kAccessibilityLargeCursorEnabled, enabled);
449 pref_service->CommitPendingWrite();
452 void AccessibilityManager::UpdateLargeCursorFromPref() {
453 if (!profile_)
454 return;
456 const bool enabled =
457 profile_->GetPrefs()->GetBoolean(prefs::kAccessibilityLargeCursorEnabled);
459 if (large_cursor_enabled_ == enabled)
460 return;
462 large_cursor_enabled_ = enabled;
464 AccessibilityStatusEventDetails details(
465 ACCESSIBILITY_TOGGLE_LARGE_CURSOR,
466 enabled,
467 ui::A11Y_NOTIFICATION_NONE);
469 NotifyAccessibilityStatusChanged(details);
471 ash::Shell::GetInstance()->cursor_manager()->SetCursorSet(
472 enabled ? ui::CURSOR_SET_LARGE : ui::CURSOR_SET_NORMAL);
473 ash::Shell::GetInstance()->SetCursorCompositingEnabled(
474 ShouldEnableCursorCompositing());
477 bool AccessibilityManager::IsIncognitoAllowed() {
478 return profile_ != NULL &&
479 profile_->GetProfileType() != Profile::GUEST_PROFILE &&
480 IncognitoModePrefs::GetAvailability(profile_->GetPrefs()) !=
481 IncognitoModePrefs::DISABLED;
484 bool AccessibilityManager::IsLargeCursorEnabled() {
485 return large_cursor_enabled_;
488 void AccessibilityManager::EnableStickyKeys(bool enabled) {
489 if (!profile_)
490 return;
491 PrefService* pref_service = profile_->GetPrefs();
492 pref_service->SetBoolean(prefs::kAccessibilityStickyKeysEnabled, enabled);
493 pref_service->CommitPendingWrite();
496 bool AccessibilityManager::IsStickyKeysEnabled() {
497 return sticky_keys_enabled_;
500 void AccessibilityManager::UpdateStickyKeysFromPref() {
501 if (!profile_)
502 return;
504 const bool enabled =
505 profile_->GetPrefs()->GetBoolean(prefs::kAccessibilityStickyKeysEnabled);
507 if (sticky_keys_enabled_ == enabled)
508 return;
510 sticky_keys_enabled_ = enabled;
511 ash::Shell::GetInstance()->sticky_keys_controller()->Enable(enabled);
514 void AccessibilityManager::EnableSpokenFeedback(
515 bool enabled,
516 ui::AccessibilityNotificationVisibility notify) {
517 if (!profile_)
518 return;
519 ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(
520 enabled ? ash::UMA_STATUS_AREA_ENABLE_SPOKEN_FEEDBACK
521 : ash::UMA_STATUS_AREA_DISABLE_SPOKEN_FEEDBACK);
523 spoken_feedback_notification_ = notify;
525 PrefService* pref_service = profile_->GetPrefs();
526 pref_service->SetBoolean(prefs::kAccessibilitySpokenFeedbackEnabled, enabled);
527 pref_service->CommitPendingWrite();
529 spoken_feedback_notification_ = ui::A11Y_NOTIFICATION_NONE;
532 void AccessibilityManager::UpdateSpokenFeedbackFromPref() {
533 if (!profile_)
534 return;
536 const bool enabled = profile_->GetPrefs()->GetBoolean(
537 prefs::kAccessibilitySpokenFeedbackEnabled);
539 if (spoken_feedback_enabled_ == enabled)
540 return;
542 spoken_feedback_enabled_ = enabled;
544 AccessibilityStatusEventDetails details(
545 ACCESSIBILITY_TOGGLE_SPOKEN_FEEDBACK,
546 enabled,
547 spoken_feedback_notification_);
549 NotifyAccessibilityStatusChanged(details);
551 if (enabled) {
552 LoadChromeVox();
553 } else {
554 UnloadChromeVox();
556 UpdateBrailleImeState();
559 void AccessibilityManager::LoadChromeVox() {
560 base::Closure done_cb = base::Bind(&AccessibilityManager::PostLoadChromeVox,
561 weak_ptr_factory_.GetWeakPtr(),
562 profile_);
563 ScreenLocker* screen_locker = ScreenLocker::default_screen_locker();
564 if (screen_locker && screen_locker->locked()) {
565 // If on the lock screen, loads ChromeVox only to the lock screen as for
566 // now. On unlock, it will be loaded to the user screen.
567 // (see. AccessibilityManager::Observe())
568 LoadChromeVoxToLockScreen(done_cb);
569 } else {
570 LoadChromeVoxToUserScreen(done_cb);
574 void AccessibilityManager::LoadChromeVoxToUserScreen(
575 const base::Closure& done_cb) {
576 if (chrome_vox_loaded_on_user_screen_)
577 return;
579 // Determine whether an OOBE screen is currently being shown. If so,
580 // ChromeVox will be injected directly into that screen.
581 content::WebUI* login_web_ui = NULL;
583 if (ProfileHelper::IsSigninProfile(profile_)) {
584 LoginDisplayHost* login_display_host = LoginDisplayHostImpl::default_host();
585 if (login_display_host) {
586 WebUILoginView* web_ui_login_view =
587 login_display_host->GetWebUILoginView();
588 if (web_ui_login_view)
589 login_web_ui = web_ui_login_view->GetWebUI();
592 // Lock screen uses the signin progile.
593 chrome_vox_loaded_on_lock_screen_ = true;
596 chrome_vox_loaded_on_user_screen_ = true;
597 LoadChromeVoxExtension(
598 profile_, login_web_ui ?
599 login_web_ui->GetWebContents()->GetRenderViewHost() : NULL,
600 done_cb);
603 void AccessibilityManager::LoadChromeVoxToLockScreen(
604 const base::Closure& done_cb) {
605 if (chrome_vox_loaded_on_lock_screen_)
606 return;
608 ScreenLocker* screen_locker = ScreenLocker::default_screen_locker();
609 if (screen_locker && screen_locker->locked()) {
610 content::WebUI* lock_web_ui = screen_locker->GetAssociatedWebUI();
611 if (lock_web_ui) {
612 Profile* profile = Profile::FromWebUI(lock_web_ui);
613 chrome_vox_loaded_on_lock_screen_ = true;
614 LoadChromeVoxExtension(
615 profile,
616 lock_web_ui->GetWebContents()->GetRenderViewHost(),
617 done_cb);
622 void AccessibilityManager::UnloadChromeVox() {
623 if (chrome_vox_loaded_on_lock_screen_)
624 UnloadChromeVoxFromLockScreen();
626 if (chrome_vox_loaded_on_user_screen_) {
627 UnloadChromeVoxExtension(profile_);
628 chrome_vox_loaded_on_user_screen_ = false;
631 PostUnloadChromeVox(profile_);
634 void AccessibilityManager::UnloadChromeVoxFromLockScreen() {
635 // Lock screen uses the signin progile.
636 Profile* signin_profile = ProfileHelper::GetSigninProfile();
637 UnloadChromeVoxExtension(signin_profile);
638 chrome_vox_loaded_on_lock_screen_ = false;
641 bool AccessibilityManager::IsSpokenFeedbackEnabled() {
642 return spoken_feedback_enabled_;
645 void AccessibilityManager::ToggleSpokenFeedback(
646 ui::AccessibilityNotificationVisibility notify) {
647 EnableSpokenFeedback(!IsSpokenFeedbackEnabled(), notify);
650 void AccessibilityManager::EnableHighContrast(bool enabled) {
651 if (!profile_)
652 return;
654 PrefService* pref_service = profile_->GetPrefs();
655 pref_service->SetBoolean(prefs::kAccessibilityHighContrastEnabled, enabled);
656 pref_service->CommitPendingWrite();
659 void AccessibilityManager::UpdateHighContrastFromPref() {
660 if (!profile_)
661 return;
663 const bool enabled = profile_->GetPrefs()->GetBoolean(
664 prefs::kAccessibilityHighContrastEnabled);
666 if (high_contrast_enabled_ == enabled)
667 return;
669 high_contrast_enabled_ = enabled;
671 AccessibilityStatusEventDetails details(
672 ACCESSIBILITY_TOGGLE_HIGH_CONTRAST_MODE,
673 enabled,
674 ui::A11Y_NOTIFICATION_NONE);
676 NotifyAccessibilityStatusChanged(details);
678 ash::Shell::GetInstance()->high_contrast_controller()->SetEnabled(enabled);
679 ash::Shell::GetInstance()->SetCursorCompositingEnabled(
680 ShouldEnableCursorCompositing());
683 void AccessibilityManager::OnLocaleChanged() {
684 if (!profile_)
685 return;
687 if (!IsSpokenFeedbackEnabled())
688 return;
690 // If the system locale changes and spoken feedback is enabled,
691 // reload ChromeVox so that it switches its internal translations
692 // to the new language.
693 EnableSpokenFeedback(false, ui::A11Y_NOTIFICATION_NONE);
694 EnableSpokenFeedback(true, ui::A11Y_NOTIFICATION_NONE);
697 void AccessibilityManager::PlayEarcon(int sound_key) {
698 DCHECK(sound_key < chromeos::SOUND_COUNT);
699 ash::PlaySystemSoundIfSpokenFeedback(sound_key);
702 bool AccessibilityManager::IsHighContrastEnabled() {
703 return high_contrast_enabled_;
706 void AccessibilityManager::EnableAutoclick(bool enabled) {
707 if (!profile_)
708 return;
710 PrefService* pref_service = profile_->GetPrefs();
711 pref_service->SetBoolean(prefs::kAccessibilityAutoclickEnabled, enabled);
712 pref_service->CommitPendingWrite();
715 bool AccessibilityManager::IsAutoclickEnabled() {
716 return autoclick_enabled_;
719 void AccessibilityManager::UpdateAutoclickFromPref() {
720 if (!profile_)
721 return;
723 bool enabled =
724 profile_->GetPrefs()->GetBoolean(prefs::kAccessibilityAutoclickEnabled);
726 if (autoclick_enabled_ == enabled)
727 return;
728 autoclick_enabled_ = enabled;
730 ash::Shell::GetInstance()->autoclick_controller()->SetEnabled(enabled);
733 void AccessibilityManager::SetAutoclickDelay(int delay_ms) {
734 if (!profile_)
735 return;
737 PrefService* pref_service = profile_->GetPrefs();
738 pref_service->SetInteger(prefs::kAccessibilityAutoclickDelayMs, delay_ms);
739 pref_service->CommitPendingWrite();
742 int AccessibilityManager::GetAutoclickDelay() const {
743 return autoclick_delay_ms_;
746 void AccessibilityManager::UpdateAutoclickDelayFromPref() {
747 if (!profile_)
748 return;
750 int autoclick_delay_ms =
751 profile_->GetPrefs()->GetInteger(prefs::kAccessibilityAutoclickDelayMs);
753 if (autoclick_delay_ms == autoclick_delay_ms_)
754 return;
755 autoclick_delay_ms_ = autoclick_delay_ms;
757 ash::Shell::GetInstance()->autoclick_controller()->SetAutoclickDelay(
758 autoclick_delay_ms_);
761 void AccessibilityManager::EnableVirtualKeyboard(bool enabled) {
762 if (!profile_)
763 return;
765 PrefService* pref_service = profile_->GetPrefs();
766 pref_service->SetBoolean(prefs::kAccessibilityVirtualKeyboardEnabled,
767 enabled);
768 pref_service->CommitPendingWrite();
771 bool AccessibilityManager::IsVirtualKeyboardEnabled() {
772 return virtual_keyboard_enabled_;
775 void AccessibilityManager::UpdateVirtualKeyboardFromPref() {
776 if (!profile_)
777 return;
779 const bool enabled = profile_->GetPrefs()->GetBoolean(
780 prefs::kAccessibilityVirtualKeyboardEnabled);
782 if (virtual_keyboard_enabled_ == enabled)
783 return;
784 virtual_keyboard_enabled_ = enabled;
786 keyboard::SetAccessibilityKeyboardEnabled(enabled);
787 // Note that there are two versions of the on-screen keyboard. A full layout
788 // is provided for accessibility, which includes sticky modifier keys to
789 // enable typing of hotkeys. A compact version is used in touchview mode
790 // to provide a layout with larger keys to facilitate touch typing. In the
791 // event that the a11y keyboard is being disabled, an on-screen keyboard might
792 // still be enabled and a forced reset is required to pick up the layout
793 // change.
794 if (keyboard::IsKeyboardEnabled())
795 ash::Shell::GetInstance()->CreateKeyboard();
796 else
797 ash::Shell::GetInstance()->DeactivateKeyboard();
799 AccessibilityStatusEventDetails details(
800 ACCESSIBILITY_TOGGLE_VIRTUAL_KEYBOARD,
801 enabled,
802 ui::A11Y_NOTIFICATION_NONE);
803 NotifyAccessibilityStatusChanged(details);
806 bool AccessibilityManager::IsBrailleDisplayConnected() const {
807 return braille_display_connected_;
810 void AccessibilityManager::CheckBrailleState() {
811 BrailleController* braille_controller = GetBrailleController();
812 if (!scoped_braille_observer_.IsObserving(braille_controller))
813 scoped_braille_observer_.Add(braille_controller);
814 BrowserThread::PostTaskAndReplyWithResult(
815 BrowserThread::IO,
816 FROM_HERE,
817 base::Bind(&BrailleController::GetDisplayState,
818 base::Unretained(braille_controller)),
819 base::Bind(&AccessibilityManager::ReceiveBrailleDisplayState,
820 weak_ptr_factory_.GetWeakPtr()));
823 void AccessibilityManager::ReceiveBrailleDisplayState(
824 scoped_ptr<extensions::api::braille_display_private::DisplayState> state) {
825 OnBrailleDisplayStateChanged(*state);
828 void AccessibilityManager::UpdateBrailleImeState() {
829 if (!profile_)
830 return;
831 PrefService* pref_service = profile_->GetPrefs();
832 std::vector<std::string> preload_engines =
833 base::SplitString(pref_service->GetString(prefs::kLanguagePreloadEngines),
834 ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
835 std::vector<std::string>::iterator it =
836 std::find(preload_engines.begin(),
837 preload_engines.end(),
838 extension_misc::kBrailleImeEngineId);
839 bool is_enabled = (it != preload_engines.end());
840 bool should_be_enabled =
841 (spoken_feedback_enabled_ && braille_display_connected_);
842 if (is_enabled == should_be_enabled)
843 return;
844 if (should_be_enabled)
845 preload_engines.push_back(extension_misc::kBrailleImeEngineId);
846 else
847 preload_engines.erase(it);
848 pref_service->SetString(prefs::kLanguagePreloadEngines,
849 base::JoinString(preload_engines, ","));
850 braille_ime_current_ = false;
853 // Overridden from InputMethodManager::Observer.
854 void AccessibilityManager::InputMethodChanged(
855 input_method::InputMethodManager* manager,
856 Profile* /* profile */,
857 bool show_message) {
858 // Sticky keys is implemented only in ash.
859 // TODO(dpolukhin): support Athena, crbug.com/408733.
860 ash::Shell::GetInstance()->sticky_keys_controller()->SetModifiersEnabled(
861 manager->IsISOLevel5ShiftUsedByCurrentInputMethod(),
862 manager->IsAltGrUsedByCurrentInputMethod());
863 const chromeos::input_method::InputMethodDescriptor descriptor =
864 manager->GetActiveIMEState()->GetCurrentInputMethod();
865 braille_ime_current_ =
866 (descriptor.id() == extension_misc::kBrailleImeEngineId);
869 void AccessibilityManager::SetProfile(Profile* profile) {
870 pref_change_registrar_.reset();
871 local_state_pref_change_registrar_.reset();
873 if (profile) {
874 // TODO(yoshiki): Move following code to PrefHandler.
875 pref_change_registrar_.reset(new PrefChangeRegistrar);
876 pref_change_registrar_->Init(profile->GetPrefs());
877 pref_change_registrar_->Add(
878 prefs::kAccessibilityLargeCursorEnabled,
879 base::Bind(&AccessibilityManager::UpdateLargeCursorFromPref,
880 base::Unretained(this)));
881 pref_change_registrar_->Add(
882 prefs::kAccessibilityStickyKeysEnabled,
883 base::Bind(&AccessibilityManager::UpdateStickyKeysFromPref,
884 base::Unretained(this)));
885 pref_change_registrar_->Add(
886 prefs::kAccessibilitySpokenFeedbackEnabled,
887 base::Bind(&AccessibilityManager::UpdateSpokenFeedbackFromPref,
888 base::Unretained(this)));
889 pref_change_registrar_->Add(
890 prefs::kAccessibilityHighContrastEnabled,
891 base::Bind(&AccessibilityManager::UpdateHighContrastFromPref,
892 base::Unretained(this)));
893 pref_change_registrar_->Add(
894 prefs::kAccessibilityAutoclickEnabled,
895 base::Bind(&AccessibilityManager::UpdateAutoclickFromPref,
896 base::Unretained(this)));
897 pref_change_registrar_->Add(
898 prefs::kAccessibilityAutoclickDelayMs,
899 base::Bind(&AccessibilityManager::UpdateAutoclickDelayFromPref,
900 base::Unretained(this)));
901 pref_change_registrar_->Add(
902 prefs::kAccessibilityVirtualKeyboardEnabled,
903 base::Bind(&AccessibilityManager::UpdateVirtualKeyboardFromPref,
904 base::Unretained(this)));
906 local_state_pref_change_registrar_.reset(new PrefChangeRegistrar);
907 local_state_pref_change_registrar_->Init(g_browser_process->local_state());
908 local_state_pref_change_registrar_->Add(
909 prefs::kApplicationLocale,
910 base::Bind(&AccessibilityManager::OnLocaleChanged,
911 base::Unretained(this)));
913 content::BrowserAccessibilityState::GetInstance()->AddHistogramCallback(
914 base::Bind(
915 &AccessibilityManager::UpdateChromeOSAccessibilityHistograms,
916 base::Unretained(this)));
919 large_cursor_pref_handler_.HandleProfileChanged(profile_, profile);
920 spoken_feedback_pref_handler_.HandleProfileChanged(profile_, profile);
921 high_contrast_pref_handler_.HandleProfileChanged(profile_, profile);
922 autoclick_pref_handler_.HandleProfileChanged(profile_, profile);
923 autoclick_delay_pref_handler_.HandleProfileChanged(profile_, profile);
924 virtual_keyboard_pref_handler_.HandleProfileChanged(profile_, profile);
926 bool had_profile = (profile_ != NULL);
927 profile_ = profile;
929 if (!had_profile && profile)
930 CheckBrailleState();
931 else
932 UpdateBrailleImeState();
933 UpdateLargeCursorFromPref();
934 UpdateStickyKeysFromPref();
935 UpdateSpokenFeedbackFromPref();
936 UpdateHighContrastFromPref();
937 UpdateAutoclickFromPref();
938 UpdateAutoclickDelayFromPref();
939 UpdateVirtualKeyboardFromPref();
942 void AccessibilityManager::ActiveUserChanged(const std::string& user_id) {
943 SetProfile(ProfileManager::GetActiveUserProfile());
946 void AccessibilityManager::SetProfileForTest(Profile* profile) {
947 SetProfile(profile);
950 void AccessibilityManager::SetBrailleControllerForTest(
951 BrailleController* controller) {
952 g_braille_controller_for_test = controller;
955 void AccessibilityManager::EnableSystemSounds(bool system_sounds_enabled) {
956 system_sounds_enabled_ = system_sounds_enabled;
959 base::TimeDelta AccessibilityManager::PlayShutdownSound() {
960 if (!system_sounds_enabled_)
961 return base::TimeDelta();
962 system_sounds_enabled_ = false;
963 if (!ash::PlaySystemSoundIfSpokenFeedback(SOUND_SHUTDOWN))
964 return base::TimeDelta();
965 return media::SoundsManager::Get()->GetDuration(SOUND_SHUTDOWN);
968 void AccessibilityManager::InjectChromeVox(RenderViewHost* render_view_host) {
969 LoadChromeVoxExtension(profile_, render_view_host, base::Closure());
972 scoped_ptr<AccessibilityStatusSubscription>
973 AccessibilityManager::RegisterCallback(
974 const AccessibilityStatusCallback& cb) {
975 return callback_list_.Add(cb);
978 void AccessibilityManager::NotifyAccessibilityStatusChanged(
979 AccessibilityStatusEventDetails& details) {
980 callback_list_.Notify(details);
983 void AccessibilityManager::UpdateChromeOSAccessibilityHistograms() {
984 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosSpokenFeedback",
985 IsSpokenFeedbackEnabled());
986 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosHighContrast",
987 IsHighContrastEnabled());
988 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosVirtualKeyboard",
989 IsVirtualKeyboardEnabled());
990 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosStickyKeys", IsStickyKeysEnabled());
991 if (MagnificationManager::Get()) {
992 uint32 type = MagnificationManager::Get()->IsMagnifierEnabled() ?
993 MagnificationManager::Get()->GetMagnifierType() : 0;
994 // '0' means magnifier is disabled.
995 UMA_HISTOGRAM_ENUMERATION("Accessibility.CrosScreenMagnifier",
996 type,
997 ui::kMaxMagnifierType + 1);
999 if (profile_) {
1000 const PrefService* const prefs = profile_->GetPrefs();
1001 UMA_HISTOGRAM_BOOLEAN(
1002 "Accessibility.CrosLargeCursor",
1003 prefs->GetBoolean(prefs::kAccessibilityLargeCursorEnabled));
1004 UMA_HISTOGRAM_BOOLEAN(
1005 "Accessibility.CrosAlwaysShowA11yMenu",
1006 prefs->GetBoolean(prefs::kShouldAlwaysShowAccessibilityMenu));
1008 bool autoclick_enabled =
1009 prefs->GetBoolean(prefs::kAccessibilityAutoclickEnabled);
1010 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosAutoclick", autoclick_enabled);
1011 if (autoclick_enabled) {
1012 // We only want to log the autoclick delay if the user has actually
1013 // enabled autoclick.
1014 UMA_HISTOGRAM_CUSTOM_TIMES(
1015 "Accessibility.CrosAutoclickDelay",
1016 base::TimeDelta::FromMilliseconds(
1017 prefs->GetInteger(prefs::kAccessibilityAutoclickDelayMs)),
1018 base::TimeDelta::FromMilliseconds(1),
1019 base::TimeDelta::FromMilliseconds(3000),
1020 50);
1025 void AccessibilityManager::Observe(
1026 int type,
1027 const content::NotificationSource& source,
1028 const content::NotificationDetails& details) {
1029 switch (type) {
1030 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
1031 // Update |profile_| when entering the login screen.
1032 Profile* profile = ProfileManager::GetActiveUserProfile();
1033 if (ProfileHelper::IsSigninProfile(profile))
1034 SetProfile(profile);
1035 break;
1037 case chrome::NOTIFICATION_SESSION_STARTED:
1038 // Update |profile_| when entering a session.
1039 SetProfile(ProfileManager::GetActiveUserProfile());
1041 // Ensure ChromeVox makes announcements at the start of new sessions.
1042 should_speak_chrome_vox_announcements_on_user_screen_ = true;
1044 // Add a session state observer to be able to monitor session changes.
1045 if (!session_state_observer_.get() && ash::Shell::HasInstance())
1046 session_state_observer_.reset(
1047 new ash::ScopedSessionStateObserver(this));
1048 break;
1049 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
1050 // Update |profile_| when exiting a session or shutting down.
1051 Profile* profile = content::Source<Profile>(source).ptr();
1052 if (profile_ == profile)
1053 SetProfile(NULL);
1054 break;
1056 case chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED: {
1057 bool is_screen_locked = *content::Details<bool>(details).ptr();
1058 if (spoken_feedback_enabled_) {
1059 if (is_screen_locked)
1060 LoadChromeVoxToLockScreen(base::Closure());
1061 // If spoken feedback was enabled, make sure it is also enabled on
1062 // the user screen.
1063 // The status tray gets verbalized by user screen ChromeVox, so we need
1064 // to load it on the user screen even if the screen is locked.
1065 LoadChromeVoxToUserScreen(base::Closure());
1067 break;
1072 void AccessibilityManager::OnBrailleDisplayStateChanged(
1073 const DisplayState& display_state) {
1074 braille_display_connected_ = display_state.available;
1075 if (braille_display_connected_) {
1076 EnableSpokenFeedback(true, ui::A11Y_NOTIFICATION_SHOW);
1078 UpdateBrailleImeState();
1080 AccessibilityStatusEventDetails details(
1081 ACCESSIBILITY_BRAILLE_DISPLAY_CONNECTION_STATE_CHANGED,
1082 braille_display_connected_,
1083 ui::A11Y_NOTIFICATION_SHOW);
1084 NotifyAccessibilityStatusChanged(details);
1087 void AccessibilityManager::OnBrailleKeyEvent(const KeyEvent& event) {
1088 // Ensure the braille IME is active on braille keyboard (dots) input.
1089 if ((event.command ==
1090 extensions::api::braille_display_private::KEY_COMMAND_DOTS) &&
1091 !braille_ime_current_) {
1092 input_method::InputMethodManager::Get()
1093 ->GetActiveIMEState()
1094 ->ChangeInputMethod(extension_misc::kBrailleImeEngineId,
1095 false /* show_message */);
1099 void AccessibilityManager::PostLoadChromeVox(Profile* profile) {
1100 // Do any setup work needed immediately after ChromeVox actually loads.
1101 ash::PlaySystemSoundAlways(SOUND_SPOKEN_FEEDBACK_ENABLED);
1103 if (chrome_vox_loaded_on_lock_screen_ ||
1104 should_speak_chrome_vox_announcements_on_user_screen_) {
1105 extensions::EventRouter* event_router =
1106 extensions::EventRouter::Get(profile);
1107 CHECK(event_router);
1109 scoped_ptr<base::ListValue> event_args(new base::ListValue());
1110 scoped_ptr<extensions::Event> event(new extensions::Event(
1111 extensions::events::ACCESSIBILITY_PRIVATE_ON_INTRODUCE_CHROME_VOX,
1112 extensions::api::accessibility_private::OnIntroduceChromeVox::
1113 kEventName,
1114 event_args.Pass()));
1115 event_router->DispatchEventWithLazyListener(
1116 extension_misc::kChromeVoxExtensionId, event.Pass());
1119 should_speak_chrome_vox_announcements_on_user_screen_ =
1120 chrome_vox_loaded_on_lock_screen_;
1123 void AccessibilityManager::PostUnloadChromeVox(Profile* profile) {
1124 // Do any teardown work needed immediately after ChromeVox actually unloads.
1125 ash::PlaySystemSoundAlways(SOUND_SPOKEN_FEEDBACK_DISABLED);
1126 // Clear the accessibility focus ring.
1127 AccessibilityFocusRingController::GetInstance()->SetFocusRing(
1128 std::vector<gfx::Rect>());
1131 } // namespace chromeos