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/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/profiles/profile.h"
40 #include "chrome/browser/profiles/profile_manager.h"
41 #include "chrome/common/chrome_paths.h"
42 #include "chrome/common/extensions/api/accessibility_private.h"
43 #include "chrome/common/extensions/extension_constants.h"
44 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
45 #include "chrome/common/pref_names.h"
46 #include "chrome/grit/browser_resources.h"
47 #include "chromeos/audio/chromeos_sounds.h"
48 #include "chromeos/ime/input_method_manager.h"
49 #include "chromeos/login/login_state.h"
50 #include "components/user_manager/user_manager.h"
51 #include "content/public/browser/browser_accessibility_state.h"
52 #include "content/public/browser/browser_thread.h"
53 #include "content/public/browser/notification_details.h"
54 #include "content/public/browser/notification_service.h"
55 #include "content/public/browser/notification_source.h"
56 #include "content/public/browser/render_process_host.h"
57 #include "content/public/browser/render_view_host.h"
58 #include "content/public/browser/web_contents.h"
59 #include "content/public/browser/web_ui.h"
60 #include "extensions/browser/extension_system.h"
61 #include "extensions/browser/file_reader.h"
62 #include "extensions/common/extension.h"
63 #include "extensions/common/extension_messages.h"
64 #include "extensions/common/extension_resource.h"
65 #include "media/audio/sounds/sounds_manager.h"
66 #include "ui/base/resource/resource_bundle.h"
67 #include "ui/keyboard/keyboard_controller.h"
68 #include "ui/keyboard/keyboard_util.h"
69
70 using content::BrowserThread;
71 using content::RenderViewHost;
72 using extensions::api::braille_display_private::BrailleController;
73 using extensions::api::braille_display_private::DisplayState;
74 using extensions::api::braille_display_private::KeyEvent;
75
76 namespace chromeos {
77
78 namespace {
79
80 static chromeos::AccessibilityManager* g_accessibility_manager = NULL;
81
82 static BrailleController* g_braille_controller_for_test = NULL;
83
GetBrailleController()84 BrailleController* GetBrailleController() {
85 return g_braille_controller_for_test
86 ? g_braille_controller_for_test
87 : BrailleController::GetInstance();
88 }
89
GetChromeVoxPath()90 base::FilePath GetChromeVoxPath() {
91 base::FilePath path;
92 if (!PathService::Get(chrome::DIR_RESOURCES, &path))
93 NOTREACHED();
94 path = path.Append(extension_misc::kChromeVoxExtensionPath);
95 return path;
96 }
97
98 // Helper class that directly loads an extension's content scripts into
99 // all of the frames corresponding to a given RenderViewHost.
100 class ContentScriptLoader {
101 public:
102 // Initialize the ContentScriptLoader with the ID of the extension
103 // and the RenderViewHost where the scripts should be loaded.
ContentScriptLoader(const std::string & extension_id,int render_process_id,int render_view_id)104 ContentScriptLoader(const std::string& extension_id,
105 int render_process_id,
106 int render_view_id)
107 : extension_id_(extension_id),
108 render_process_id_(render_process_id),
109 render_view_id_(render_view_id) {}
110
111 // Call this once with the ExtensionResource corresponding to each
112 // content script to be loaded.
AppendScript(extensions::ExtensionResource resource)113 void AppendScript(extensions::ExtensionResource resource) {
114 resources_.push(resource);
115 }
116
117 // Finally, call this method once to fetch all of the resources and
118 // load them. This method will delete this object when done.
Run()119 void Run() {
120 if (resources_.empty()) {
121 delete this;
122 return;
123 }
124
125 extensions::ExtensionResource resource = resources_.front();
126 resources_.pop();
127 scoped_refptr<FileReader> reader(new FileReader(resource, base::Bind(
128 &ContentScriptLoader::OnFileLoaded, base::Unretained(this))));
129 reader->Start();
130 }
131
132 private:
OnFileLoaded(bool success,const std::string & data)133 void OnFileLoaded(bool success, const std::string& data) {
134 if (success) {
135 ExtensionMsg_ExecuteCode_Params params;
136 params.request_id = 0;
137 params.extension_id = extension_id_;
138 params.is_javascript = true;
139 params.code = data;
140 params.run_at = extensions::UserScript::DOCUMENT_IDLE;
141 params.all_frames = true;
142 params.match_about_blank = false;
143 params.in_main_world = false;
144
145 RenderViewHost* render_view_host =
146 RenderViewHost::FromID(render_process_id_, render_view_id_);
147 if (render_view_host) {
148 render_view_host->Send(new ExtensionMsg_ExecuteCode(
149 render_view_host->GetRoutingID(), params));
150 }
151 }
152 Run();
153 }
154
155 std::string extension_id_;
156 int render_process_id_;
157 int render_view_id_;
158 std::queue<extensions::ExtensionResource> resources_;
159 };
160
161 void InjectChromeVoxContentScript(
162 ExtensionService* extension_service,
163 int render_process_id,
164 int render_view_id,
165 const base::Closure& done_cb);
166
LoadChromeVoxExtension(Profile * profile,RenderViewHost * render_view_host,base::Closure done_cb)167 void LoadChromeVoxExtension(
168 Profile* profile,
169 RenderViewHost* render_view_host,
170 base::Closure done_cb) {
171 ExtensionService* extension_service =
172 extensions::ExtensionSystem::Get(profile)->extension_service();
173 if (render_view_host) {
174 // Wrap the passed in callback to inject the content script.
175 done_cb = base::Bind(
176 &InjectChromeVoxContentScript,
177 extension_service,
178 render_view_host->GetProcess()->GetID(),
179 render_view_host->GetRoutingID(),
180 done_cb);
181 }
182 extension_service->component_loader()->AddChromeVoxExtension(done_cb);
183 }
184
InjectChromeVoxContentScript(ExtensionService * extension_service,int render_process_id,int render_view_id,const base::Closure & done_cb)185 void InjectChromeVoxContentScript(
186 ExtensionService* extension_service,
187 int render_process_id,
188 int render_view_id,
189 const base::Closure& done_cb) {
190 // Make sure to always run |done_cb|. ChromeVox was loaded even if we end up
191 // not injecting into this particular render view.
192 base::ScopedClosureRunner done_runner(done_cb);
193 RenderViewHost* render_view_host =
194 RenderViewHost::FromID(render_process_id, render_view_id);
195 if (!render_view_host)
196 return;
197 const extensions::Extension* extension =
198 extension_service->extensions()->GetByID(
199 extension_misc::kChromeVoxExtensionId);
200
201 // Set a flag to tell ChromeVox that it's just been enabled,
202 // so that it won't interrupt our speech feedback enabled message.
203 ExtensionMsg_ExecuteCode_Params params;
204 params.request_id = 0;
205 params.extension_id = extension->id();
206 params.is_javascript = true;
207 params.code = "window.INJECTED_AFTER_LOAD = true;";
208 params.run_at = extensions::UserScript::DOCUMENT_IDLE;
209 params.all_frames = true;
210 params.match_about_blank = false;
211 params.in_main_world = false;
212 render_view_host->Send(new ExtensionMsg_ExecuteCode(
213 render_view_host->GetRoutingID(), params));
214
215 // Inject ChromeVox' content scripts.
216 ContentScriptLoader* loader = new ContentScriptLoader(
217 extension->id(), render_view_host->GetProcess()->GetID(),
218 render_view_host->GetRoutingID());
219
220 const extensions::UserScriptList& content_scripts =
221 extensions::ContentScriptsInfo::GetContentScripts(extension);
222 for (size_t i = 0; i < content_scripts.size(); i++) {
223 const extensions::UserScript& script = content_scripts[i];
224 for (size_t j = 0; j < script.js_scripts().size(); ++j) {
225 const extensions::UserScript::File& file = script.js_scripts()[j];
226 extensions::ExtensionResource resource = extension->GetResource(
227 file.relative_path());
228 loader->AppendScript(resource);
229 }
230 }
231 loader->Run(); // It cleans itself up when done.
232 }
233
UnloadChromeVoxExtension(Profile * profile)234 void UnloadChromeVoxExtension(Profile* profile) {
235 base::FilePath path = GetChromeVoxPath();
236 ExtensionService* extension_service =
237 extensions::ExtensionSystem::Get(profile)->extension_service();
238 extension_service->component_loader()->Remove(path);
239 }
240
241 } // namespace
242
243 ///////////////////////////////////////////////////////////////////////////////
244 // AccessibilityStatusEventDetails
245
AccessibilityStatusEventDetails(AccessibilityNotificationType notification_type,bool enabled,ash::AccessibilityNotificationVisibility notify)246 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
247 AccessibilityNotificationType notification_type,
248 bool enabled,
249 ash::AccessibilityNotificationVisibility notify)
250 : notification_type(notification_type),
251 enabled(enabled),
252 magnifier_type(ash::kDefaultMagnifierType),
253 notify(notify) {}
254
AccessibilityStatusEventDetails(AccessibilityNotificationType notification_type,bool enabled,ash::MagnifierType magnifier_type,ash::AccessibilityNotificationVisibility notify)255 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
256 AccessibilityNotificationType notification_type,
257 bool enabled,
258 ash::MagnifierType magnifier_type,
259 ash::AccessibilityNotificationVisibility notify)
260 : notification_type(notification_type),
261 enabled(enabled),
262 magnifier_type(magnifier_type),
263 notify(notify) {}
264
265 ///////////////////////////////////////////////////////////////////////////////
266 //
267 // AccessibilityManager::PrefHandler
268
PrefHandler(const char * pref_path)269 AccessibilityManager::PrefHandler::PrefHandler(const char* pref_path)
270 : pref_path_(pref_path) {}
271
~PrefHandler()272 AccessibilityManager::PrefHandler::~PrefHandler() {}
273
HandleProfileChanged(Profile * previous_profile,Profile * current_profile)274 void AccessibilityManager::PrefHandler::HandleProfileChanged(
275 Profile* previous_profile, Profile* current_profile) {
276 // Returns if the current profile is null.
277 if (!current_profile)
278 return;
279
280 // If the user set a pref value on the login screen and is now starting a
281 // session with a new profile, copy the pref value to the profile.
282 if ((previous_profile &&
283 ProfileHelper::IsSigninProfile(previous_profile) &&
284 current_profile->IsNewProfile() &&
285 !ProfileHelper::IsSigninProfile(current_profile)) ||
286 // Special case for Guest mode:
287 // Guest mode launches a guest-mode browser process before session starts,
288 // so the previous profile is null.
289 (!previous_profile &&
290 current_profile->IsGuestSession())) {
291 // Returns if the pref has not been set by the user.
292 const PrefService::Preference* pref = ProfileHelper::GetSigninProfile()->
293 GetPrefs()->FindPreference(pref_path_);
294 if (!pref || !pref->IsUserControlled())
295 return;
296
297 // Copy the pref value from the signin screen.
298 const base::Value* value_on_login = pref->GetValue();
299 PrefService* user_prefs = current_profile->GetPrefs();
300 user_prefs->Set(pref_path_, *value_on_login);
301 }
302 }
303
304 ///////////////////////////////////////////////////////////////////////////////
305 //
306 // AccessibilityManager
307
308 // static
Initialize()309 void AccessibilityManager::Initialize() {
310 CHECK(g_accessibility_manager == NULL);
311 g_accessibility_manager = new AccessibilityManager();
312 }
313
314 // static
Shutdown()315 void AccessibilityManager::Shutdown() {
316 CHECK(g_accessibility_manager);
317 delete g_accessibility_manager;
318 g_accessibility_manager = NULL;
319 }
320
321 // static
Get()322 AccessibilityManager* AccessibilityManager::Get() {
323 return g_accessibility_manager;
324 }
325
AccessibilityManager()326 AccessibilityManager::AccessibilityManager()
327 : profile_(NULL),
328 chrome_vox_loaded_on_lock_screen_(false),
329 chrome_vox_loaded_on_user_screen_(false),
330 large_cursor_pref_handler_(prefs::kAccessibilityLargeCursorEnabled),
331 spoken_feedback_pref_handler_(prefs::kAccessibilitySpokenFeedbackEnabled),
332 high_contrast_pref_handler_(prefs::kAccessibilityHighContrastEnabled),
333 autoclick_pref_handler_(prefs::kAccessibilityAutoclickEnabled),
334 autoclick_delay_pref_handler_(prefs::kAccessibilityAutoclickDelayMs),
335 virtual_keyboard_pref_handler_(
336 prefs::kAccessibilityVirtualKeyboardEnabled),
337 large_cursor_enabled_(false),
338 sticky_keys_enabled_(false),
339 spoken_feedback_enabled_(false),
340 high_contrast_enabled_(false),
341 autoclick_enabled_(false),
342 autoclick_delay_ms_(ash::AutoclickController::kDefaultAutoclickDelayMs),
343 virtual_keyboard_enabled_(false),
344 spoken_feedback_notification_(ash::A11Y_NOTIFICATION_NONE),
345 should_speak_chrome_vox_announcements_on_user_screen_(true),
346 system_sounds_enabled_(false),
347 braille_display_connected_(false),
348 scoped_braille_observer_(this),
349 braille_ime_current_(false),
350 weak_ptr_factory_(this) {
351 notification_registrar_.Add(this,
352 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
353 content::NotificationService::AllSources());
354 notification_registrar_.Add(this,
355 chrome::NOTIFICATION_SESSION_STARTED,
356 content::NotificationService::AllSources());
357 notification_registrar_.Add(this,
358 chrome::NOTIFICATION_PROFILE_DESTROYED,
359 content::NotificationService::AllSources());
360 notification_registrar_.Add(this,
361 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED,
362 content::NotificationService::AllSources());
363
364 input_method::InputMethodManager::Get()->AddObserver(this);
365
366 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
367 media::SoundsManager* manager = media::SoundsManager::Get();
368 manager->Initialize(SOUND_SHUTDOWN,
369 bundle.GetRawDataResource(IDR_SOUND_SHUTDOWN_WAV));
370 manager->Initialize(
371 SOUND_SPOKEN_FEEDBACK_ENABLED,
372 bundle.GetRawDataResource(IDR_SOUND_SPOKEN_FEEDBACK_ENABLED_WAV));
373 manager->Initialize(
374 SOUND_SPOKEN_FEEDBACK_DISABLED,
375 bundle.GetRawDataResource(IDR_SOUND_SPOKEN_FEEDBACK_DISABLED_WAV));
376 manager->Initialize(SOUND_PASSTHROUGH,
377 bundle.GetRawDataResource(IDR_SOUND_PASSTHROUGH_WAV));
378 manager->Initialize(SOUND_EXIT_SCREEN,
379 bundle.GetRawDataResource(IDR_SOUND_EXIT_SCREEN_WAV));
380 manager->Initialize(SOUND_ENTER_SCREEN,
381 bundle.GetRawDataResource(IDR_SOUND_ENTER_SCREEN_WAV));
382 }
383
~AccessibilityManager()384 AccessibilityManager::~AccessibilityManager() {
385 CHECK(this == g_accessibility_manager);
386 AccessibilityStatusEventDetails details(
387 ACCESSIBILITY_MANAGER_SHUTDOWN,
388 false,
389 ash::A11Y_NOTIFICATION_NONE);
390 NotifyAccessibilityStatusChanged(details);
391 input_method::InputMethodManager::Get()->RemoveObserver(this);
392 }
393
ShouldShowAccessibilityMenu()394 bool AccessibilityManager::ShouldShowAccessibilityMenu() {
395 // If any of the loaded profiles has an accessibility feature turned on - or
396 // enforced to always show the menu - we return true to show the menu.
397 std::vector<Profile*> profiles =
398 g_browser_process->profile_manager()->GetLoadedProfiles();
399 for (std::vector<Profile*>::iterator it = profiles.begin();
400 it != profiles.end();
401 ++it) {
402 PrefService* pref_service = (*it)->GetPrefs();
403 if (pref_service->GetBoolean(prefs::kAccessibilityStickyKeysEnabled) ||
404 pref_service->GetBoolean(prefs::kAccessibilityLargeCursorEnabled) ||
405 pref_service->GetBoolean(prefs::kAccessibilitySpokenFeedbackEnabled) ||
406 pref_service->GetBoolean(prefs::kAccessibilityHighContrastEnabled) ||
407 pref_service->GetBoolean(prefs::kAccessibilityAutoclickEnabled) ||
408 pref_service->GetBoolean(prefs::kShouldAlwaysShowAccessibilityMenu) ||
409 pref_service->GetBoolean(prefs::kAccessibilityScreenMagnifierEnabled) ||
410 pref_service->GetBoolean(prefs::kAccessibilityVirtualKeyboardEnabled))
411 return true;
412 }
413 return false;
414 }
415
ShouldEnableCursorCompositing()416 bool AccessibilityManager::ShouldEnableCursorCompositing() {
417 #if defined(OS_CHROMEOS)
418 if (!profile_)
419 return false;
420 PrefService* pref_service = profile_->GetPrefs();
421 // Enable cursor compositing when one or more of the listed accessibility
422 // features are turned on.
423 if (pref_service->GetBoolean(prefs::kAccessibilityLargeCursorEnabled) ||
424 pref_service->GetBoolean(prefs::kAccessibilityHighContrastEnabled) ||
425 pref_service->GetBoolean(prefs::kAccessibilityScreenMagnifierEnabled))
426 return true;
427 #endif
428 return false;
429 }
430
EnableLargeCursor(bool enabled)431 void AccessibilityManager::EnableLargeCursor(bool enabled) {
432 if (!profile_)
433 return;
434
435 PrefService* pref_service = profile_->GetPrefs();
436 pref_service->SetBoolean(prefs::kAccessibilityLargeCursorEnabled, enabled);
437 pref_service->CommitPendingWrite();
438 }
439
UpdateLargeCursorFromPref()440 void AccessibilityManager::UpdateLargeCursorFromPref() {
441 if (!profile_)
442 return;
443
444 const bool enabled =
445 profile_->GetPrefs()->GetBoolean(prefs::kAccessibilityLargeCursorEnabled);
446
447 if (large_cursor_enabled_ == enabled)
448 return;
449
450 large_cursor_enabled_ = enabled;
451
452 AccessibilityStatusEventDetails details(
453 ACCESSIBILITY_TOGGLE_LARGE_CURSOR,
454 enabled,
455 ash::A11Y_NOTIFICATION_NONE);
456
457 NotifyAccessibilityStatusChanged(details);
458 #if !defined(USE_ATHENA)
459 // crbug.com/408733 (and for all USE_ATHENA in this file)
460
461 #if defined(USE_ASH)
462 // Large cursor is implemented only in ash.
463 ash::Shell::GetInstance()->cursor_manager()->SetCursorSet(
464 enabled ? ui::CURSOR_SET_LARGE : ui::CURSOR_SET_NORMAL);
465 #endif
466
467 #if defined(OS_CHROMEOS)
468 ash::Shell::GetInstance()->SetCursorCompositingEnabled(
469 ShouldEnableCursorCompositing());
470 #endif
471
472 #endif // !USE_ATHENA
473 }
474
IsIncognitoAllowed()475 bool AccessibilityManager::IsIncognitoAllowed() {
476 // Supervised users can't create incognito-mode windows.
477 return !(user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser());
478 }
479
IsLargeCursorEnabled()480 bool AccessibilityManager::IsLargeCursorEnabled() {
481 return large_cursor_enabled_;
482 }
483
EnableStickyKeys(bool enabled)484 void AccessibilityManager::EnableStickyKeys(bool enabled) {
485 if (!profile_)
486 return;
487 PrefService* pref_service = profile_->GetPrefs();
488 pref_service->SetBoolean(prefs::kAccessibilityStickyKeysEnabled, enabled);
489 pref_service->CommitPendingWrite();
490 }
491
IsStickyKeysEnabled()492 bool AccessibilityManager::IsStickyKeysEnabled() {
493 return sticky_keys_enabled_;
494 }
495
UpdateStickyKeysFromPref()496 void AccessibilityManager::UpdateStickyKeysFromPref() {
497 if (!profile_)
498 return;
499
500 const bool enabled =
501 profile_->GetPrefs()->GetBoolean(prefs::kAccessibilityStickyKeysEnabled);
502
503 if (sticky_keys_enabled_ == enabled)
504 return;
505
506 sticky_keys_enabled_ = enabled;
507 #if defined(USE_ASH) && !defined(USE_ATHENA)
508 ash::Shell::GetInstance()->sticky_keys_controller()->Enable(enabled);
509 #endif
510 }
511
EnableSpokenFeedback(bool enabled,ash::AccessibilityNotificationVisibility notify)512 void AccessibilityManager::EnableSpokenFeedback(
513 bool enabled,
514 ash::AccessibilityNotificationVisibility notify) {
515 if (!profile_)
516 return;
517 #if !defined(USE_ATHENA)
518 ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(
519 enabled ? ash::UMA_STATUS_AREA_ENABLE_SPOKEN_FEEDBACK
520 : ash::UMA_STATUS_AREA_DISABLE_SPOKEN_FEEDBACK);
521 #endif
522
523 spoken_feedback_notification_ = notify;
524
525 PrefService* pref_service = profile_->GetPrefs();
526 pref_service->SetBoolean(prefs::kAccessibilitySpokenFeedbackEnabled, enabled);
527 pref_service->CommitPendingWrite();
528
529 spoken_feedback_notification_ = ash::A11Y_NOTIFICATION_NONE;
530 }
531
UpdateSpokenFeedbackFromPref()532 void AccessibilityManager::UpdateSpokenFeedbackFromPref() {
533 if (!profile_)
534 return;
535
536 const bool enabled = profile_->GetPrefs()->GetBoolean(
537 prefs::kAccessibilitySpokenFeedbackEnabled);
538
539 if (spoken_feedback_enabled_ == enabled)
540 return;
541
542 spoken_feedback_enabled_ = enabled;
543
544 ExtensionAccessibilityEventRouter::GetInstance()->
545 SetAccessibilityEnabled(enabled);
546
547 AccessibilityStatusEventDetails details(
548 ACCESSIBILITY_TOGGLE_SPOKEN_FEEDBACK,
549 enabled,
550 spoken_feedback_notification_);
551
552 NotifyAccessibilityStatusChanged(details);
553
554 if (enabled) {
555 LoadChromeVox();
556 } else {
557 UnloadChromeVox();
558 }
559 UpdateBrailleImeState();
560 }
561
LoadChromeVox()562 void AccessibilityManager::LoadChromeVox() {
563 base::Closure done_cb = base::Bind(&AccessibilityManager::PostLoadChromeVox,
564 weak_ptr_factory_.GetWeakPtr(),
565 profile_);
566 ScreenLocker* screen_locker = ScreenLocker::default_screen_locker();
567 if (screen_locker && screen_locker->locked()) {
568 // If on the lock screen, loads ChromeVox only to the lock screen as for
569 // now. On unlock, it will be loaded to the user screen.
570 // (see. AccessibilityManager::Observe())
571 LoadChromeVoxToLockScreen(done_cb);
572 } else {
573 LoadChromeVoxToUserScreen(done_cb);
574 }
575 }
576
LoadChromeVoxToUserScreen(const base::Closure & done_cb)577 void AccessibilityManager::LoadChromeVoxToUserScreen(
578 const base::Closure& done_cb) {
579 if (chrome_vox_loaded_on_user_screen_)
580 return;
581
582 // Determine whether an OOBE screen is currently being shown. If so,
583 // ChromeVox will be injected directly into that screen.
584 content::WebUI* login_web_ui = NULL;
585
586 if (ProfileHelper::IsSigninProfile(profile_)) {
587 LoginDisplayHost* login_display_host = LoginDisplayHostImpl::default_host();
588 if (login_display_host) {
589 WebUILoginView* web_ui_login_view =
590 login_display_host->GetWebUILoginView();
591 if (web_ui_login_view)
592 login_web_ui = web_ui_login_view->GetWebUI();
593 }
594
595 // Lock screen uses the signin progile.
596 chrome_vox_loaded_on_lock_screen_ = true;
597 }
598
599 chrome_vox_loaded_on_user_screen_ = true;
600 LoadChromeVoxExtension(
601 profile_, login_web_ui ?
602 login_web_ui->GetWebContents()->GetRenderViewHost() : NULL,
603 done_cb);
604 }
605
LoadChromeVoxToLockScreen(const base::Closure & done_cb)606 void AccessibilityManager::LoadChromeVoxToLockScreen(
607 const base::Closure& done_cb) {
608 if (chrome_vox_loaded_on_lock_screen_)
609 return;
610
611 ScreenLocker* screen_locker = ScreenLocker::default_screen_locker();
612 if (screen_locker && screen_locker->locked()) {
613 content::WebUI* lock_web_ui = screen_locker->GetAssociatedWebUI();
614 if (lock_web_ui) {
615 Profile* profile = Profile::FromWebUI(lock_web_ui);
616 chrome_vox_loaded_on_lock_screen_ = true;
617 LoadChromeVoxExtension(
618 profile,
619 lock_web_ui->GetWebContents()->GetRenderViewHost(),
620 done_cb);
621 }
622 }
623 }
624
UnloadChromeVox()625 void AccessibilityManager::UnloadChromeVox() {
626 if (chrome_vox_loaded_on_lock_screen_)
627 UnloadChromeVoxFromLockScreen();
628
629 if (chrome_vox_loaded_on_user_screen_) {
630 UnloadChromeVoxExtension(profile_);
631 chrome_vox_loaded_on_user_screen_ = false;
632 }
633
634 PostUnloadChromeVox(profile_);
635 }
636
UnloadChromeVoxFromLockScreen()637 void AccessibilityManager::UnloadChromeVoxFromLockScreen() {
638 // Lock screen uses the signin progile.
639 Profile* signin_profile = ProfileHelper::GetSigninProfile();
640 UnloadChromeVoxExtension(signin_profile);
641 chrome_vox_loaded_on_lock_screen_ = false;
642 }
643
IsSpokenFeedbackEnabled()644 bool AccessibilityManager::IsSpokenFeedbackEnabled() {
645 return spoken_feedback_enabled_;
646 }
647
ToggleSpokenFeedback(ash::AccessibilityNotificationVisibility notify)648 void AccessibilityManager::ToggleSpokenFeedback(
649 ash::AccessibilityNotificationVisibility notify) {
650 EnableSpokenFeedback(!IsSpokenFeedbackEnabled(), notify);
651 }
652
EnableHighContrast(bool enabled)653 void AccessibilityManager::EnableHighContrast(bool enabled) {
654 if (!profile_)
655 return;
656
657 PrefService* pref_service = profile_->GetPrefs();
658 pref_service->SetBoolean(prefs::kAccessibilityHighContrastEnabled, enabled);
659 pref_service->CommitPendingWrite();
660 }
661
UpdateHighContrastFromPref()662 void AccessibilityManager::UpdateHighContrastFromPref() {
663 if (!profile_)
664 return;
665
666 const bool enabled = profile_->GetPrefs()->GetBoolean(
667 prefs::kAccessibilityHighContrastEnabled);
668
669 if (high_contrast_enabled_ == enabled)
670 return;
671
672 high_contrast_enabled_ = enabled;
673
674 AccessibilityStatusEventDetails details(
675 ACCESSIBILITY_TOGGLE_HIGH_CONTRAST_MODE,
676 enabled,
677 ash::A11Y_NOTIFICATION_NONE);
678
679 NotifyAccessibilityStatusChanged(details);
680
681 #if !defined(USE_ATHENA)
682
683 #if defined(USE_ASH)
684 ash::Shell::GetInstance()->high_contrast_controller()->SetEnabled(enabled);
685 #endif
686
687 #if defined(OS_CHROMEOS)
688 ash::Shell::GetInstance()->SetCursorCompositingEnabled(
689 ShouldEnableCursorCompositing());
690 #endif
691
692 #endif
693 }
694
OnLocaleChanged()695 void AccessibilityManager::OnLocaleChanged() {
696 if (!profile_)
697 return;
698
699 if (!IsSpokenFeedbackEnabled())
700 return;
701
702 // If the system locale changes and spoken feedback is enabled,
703 // reload ChromeVox so that it switches its internal translations
704 // to the new language.
705 EnableSpokenFeedback(false, ash::A11Y_NOTIFICATION_NONE);
706 EnableSpokenFeedback(true, ash::A11Y_NOTIFICATION_NONE);
707 }
708
PlayEarcon(int sound_key)709 void AccessibilityManager::PlayEarcon(int sound_key) {
710 DCHECK(sound_key < chromeos::SOUND_COUNT);
711 ash::PlaySystemSoundIfSpokenFeedback(sound_key);
712 }
713
IsHighContrastEnabled()714 bool AccessibilityManager::IsHighContrastEnabled() {
715 return high_contrast_enabled_;
716 }
717
EnableAutoclick(bool enabled)718 void AccessibilityManager::EnableAutoclick(bool enabled) {
719 if (!profile_)
720 return;
721
722 PrefService* pref_service = profile_->GetPrefs();
723 pref_service->SetBoolean(prefs::kAccessibilityAutoclickEnabled, enabled);
724 pref_service->CommitPendingWrite();
725 }
726
IsAutoclickEnabled()727 bool AccessibilityManager::IsAutoclickEnabled() {
728 return autoclick_enabled_;
729 }
730
UpdateAutoclickFromPref()731 void AccessibilityManager::UpdateAutoclickFromPref() {
732 bool enabled =
733 profile_->GetPrefs()->GetBoolean(prefs::kAccessibilityAutoclickEnabled);
734
735 if (autoclick_enabled_ == enabled)
736 return;
737 autoclick_enabled_ = enabled;
738
739 #if defined(USE_ASH) && !defined(USE_ATHENA)
740 ash::Shell::GetInstance()->autoclick_controller()->SetEnabled(enabled);
741 #endif
742 }
743
SetAutoclickDelay(int delay_ms)744 void AccessibilityManager::SetAutoclickDelay(int delay_ms) {
745 if (!profile_)
746 return;
747
748 PrefService* pref_service = profile_->GetPrefs();
749 pref_service->SetInteger(prefs::kAccessibilityAutoclickDelayMs, delay_ms);
750 pref_service->CommitPendingWrite();
751 }
752
GetAutoclickDelay() const753 int AccessibilityManager::GetAutoclickDelay() const {
754 return autoclick_delay_ms_;
755 }
756
UpdateAutoclickDelayFromPref()757 void AccessibilityManager::UpdateAutoclickDelayFromPref() {
758 int autoclick_delay_ms =
759 profile_->GetPrefs()->GetInteger(prefs::kAccessibilityAutoclickDelayMs);
760
761 if (autoclick_delay_ms == autoclick_delay_ms_)
762 return;
763 autoclick_delay_ms_ = autoclick_delay_ms;
764
765 #if defined(USE_ASH) && !defined(USE_ATHENA)
766 ash::Shell::GetInstance()->autoclick_controller()->SetAutoclickDelay(
767 autoclick_delay_ms_);
768 #endif
769 }
770
EnableVirtualKeyboard(bool enabled)771 void AccessibilityManager::EnableVirtualKeyboard(bool enabled) {
772 if (!profile_)
773 return;
774
775 PrefService* pref_service = profile_->GetPrefs();
776 pref_service->SetBoolean(prefs::kAccessibilityVirtualKeyboardEnabled,
777 enabled);
778 pref_service->CommitPendingWrite();
779 }
780
IsVirtualKeyboardEnabled()781 bool AccessibilityManager::IsVirtualKeyboardEnabled() {
782 return virtual_keyboard_enabled_;
783 }
784
UpdateVirtualKeyboardFromPref()785 void AccessibilityManager::UpdateVirtualKeyboardFromPref() {
786 if (!profile_)
787 return;
788
789 const bool enabled = profile_->GetPrefs()->GetBoolean(
790 prefs::kAccessibilityVirtualKeyboardEnabled);
791
792 if (virtual_keyboard_enabled_ == enabled)
793 return;
794 virtual_keyboard_enabled_ = enabled;
795
796 AccessibilityStatusEventDetails details(
797 ACCESSIBILITY_TOGGLE_VIRTUAL_KEYBOARD,
798 enabled,
799 ash::A11Y_NOTIFICATION_NONE);
800
801 NotifyAccessibilityStatusChanged(details);
802
803 #if defined(USE_ASH) && !defined(USE_ATHENA)
804 keyboard::SetAccessibilityKeyboardEnabled(enabled);
805 // Note that there are two versions of the on-screen keyboard. A full layout
806 // is provided for accessibility, which includes sticky modifier keys to
807 // enable typing of hotkeys. A compact version is used in touchview mode
808 // to provide a layout with larger keys to facilitate touch typing. In the
809 // event that the a11y keyboard is being disabled, an on-screen keyboard might
810 // still be enabled and a forced reset is required to pick up the layout
811 // change.
812 if (keyboard::IsKeyboardEnabled())
813 ash::Shell::GetInstance()->CreateKeyboard();
814 else
815 ash::Shell::GetInstance()->DeactivateKeyboard();
816 #endif
817 }
818
IsBrailleDisplayConnected() const819 bool AccessibilityManager::IsBrailleDisplayConnected() const {
820 return braille_display_connected_;
821 }
822
CheckBrailleState()823 void AccessibilityManager::CheckBrailleState() {
824 BrailleController* braille_controller = GetBrailleController();
825 if (!scoped_braille_observer_.IsObserving(braille_controller))
826 scoped_braille_observer_.Add(braille_controller);
827 BrowserThread::PostTaskAndReplyWithResult(
828 BrowserThread::IO,
829 FROM_HERE,
830 base::Bind(&BrailleController::GetDisplayState,
831 base::Unretained(braille_controller)),
832 base::Bind(&AccessibilityManager::ReceiveBrailleDisplayState,
833 weak_ptr_factory_.GetWeakPtr()));
834 }
835
ReceiveBrailleDisplayState(scoped_ptr<extensions::api::braille_display_private::DisplayState> state)836 void AccessibilityManager::ReceiveBrailleDisplayState(
837 scoped_ptr<extensions::api::braille_display_private::DisplayState> state) {
838 OnBrailleDisplayStateChanged(*state);
839 }
840
UpdateBrailleImeState()841 void AccessibilityManager::UpdateBrailleImeState() {
842 if (!profile_)
843 return;
844 PrefService* pref_service = profile_->GetPrefs();
845 std::vector<std::string> preload_engines;
846 base::SplitString(pref_service->GetString(prefs::kLanguagePreloadEngines),
847 ',',
848 &preload_engines);
849 std::vector<std::string>::iterator it =
850 std::find(preload_engines.begin(),
851 preload_engines.end(),
852 extension_misc::kBrailleImeEngineId);
853 bool is_enabled = (it != preload_engines.end());
854 bool should_be_enabled =
855 (spoken_feedback_enabled_ && braille_display_connected_);
856 if (is_enabled == should_be_enabled)
857 return;
858 if (should_be_enabled)
859 preload_engines.push_back(extension_misc::kBrailleImeEngineId);
860 else
861 preload_engines.erase(it);
862 pref_service->SetString(prefs::kLanguagePreloadEngines,
863 JoinString(preload_engines, ','));
864 braille_ime_current_ = false;
865 }
866
867 // Overridden from InputMethodManager::Observer.
InputMethodChanged(input_method::InputMethodManager * manager,bool show_message)868 void AccessibilityManager::InputMethodChanged(
869 input_method::InputMethodManager* manager,
870 bool show_message) {
871 #if defined(USE_ASH) && !defined(USE_ATHENA)
872 // Sticky keys is implemented only in ash.
873 // TODO(dpolukhin): support Athena, crbug.com/408733.
874 ash::Shell::GetInstance()->sticky_keys_controller()->SetModifiersEnabled(
875 manager->IsISOLevel5ShiftUsedByCurrentInputMethod(),
876 manager->IsAltGrUsedByCurrentInputMethod());
877 #endif
878 const chromeos::input_method::InputMethodDescriptor descriptor =
879 manager->GetActiveIMEState()->GetCurrentInputMethod();
880 braille_ime_current_ =
881 (descriptor.id() == extension_misc::kBrailleImeEngineId);
882 }
883
SetProfile(Profile * profile)884 void AccessibilityManager::SetProfile(Profile* profile) {
885 pref_change_registrar_.reset();
886 local_state_pref_change_registrar_.reset();
887
888 if (profile) {
889 // TODO(yoshiki): Move following code to PrefHandler.
890 pref_change_registrar_.reset(new PrefChangeRegistrar);
891 pref_change_registrar_->Init(profile->GetPrefs());
892 pref_change_registrar_->Add(
893 prefs::kAccessibilityLargeCursorEnabled,
894 base::Bind(&AccessibilityManager::UpdateLargeCursorFromPref,
895 base::Unretained(this)));
896 pref_change_registrar_->Add(
897 prefs::kAccessibilityStickyKeysEnabled,
898 base::Bind(&AccessibilityManager::UpdateStickyKeysFromPref,
899 base::Unretained(this)));
900 pref_change_registrar_->Add(
901 prefs::kAccessibilitySpokenFeedbackEnabled,
902 base::Bind(&AccessibilityManager::UpdateSpokenFeedbackFromPref,
903 base::Unretained(this)));
904 pref_change_registrar_->Add(
905 prefs::kAccessibilityHighContrastEnabled,
906 base::Bind(&AccessibilityManager::UpdateHighContrastFromPref,
907 base::Unretained(this)));
908 pref_change_registrar_->Add(
909 prefs::kAccessibilityAutoclickEnabled,
910 base::Bind(&AccessibilityManager::UpdateAutoclickFromPref,
911 base::Unretained(this)));
912 pref_change_registrar_->Add(
913 prefs::kAccessibilityAutoclickDelayMs,
914 base::Bind(&AccessibilityManager::UpdateAutoclickDelayFromPref,
915 base::Unretained(this)));
916 pref_change_registrar_->Add(
917 prefs::kAccessibilityVirtualKeyboardEnabled,
918 base::Bind(&AccessibilityManager::UpdateVirtualKeyboardFromPref,
919 base::Unretained(this)));
920
921 local_state_pref_change_registrar_.reset(new PrefChangeRegistrar);
922 local_state_pref_change_registrar_->Init(g_browser_process->local_state());
923 local_state_pref_change_registrar_->Add(
924 prefs::kApplicationLocale,
925 base::Bind(&AccessibilityManager::OnLocaleChanged,
926 base::Unretained(this)));
927
928 content::BrowserAccessibilityState::GetInstance()->AddHistogramCallback(
929 base::Bind(
930 &AccessibilityManager::UpdateChromeOSAccessibilityHistograms,
931 base::Unretained(this)));
932 }
933
934 large_cursor_pref_handler_.HandleProfileChanged(profile_, profile);
935 spoken_feedback_pref_handler_.HandleProfileChanged(profile_, profile);
936 high_contrast_pref_handler_.HandleProfileChanged(profile_, profile);
937 autoclick_pref_handler_.HandleProfileChanged(profile_, profile);
938 autoclick_delay_pref_handler_.HandleProfileChanged(profile_, profile);
939 virtual_keyboard_pref_handler_.HandleProfileChanged(profile_, profile);
940
941 bool had_profile = (profile_ != NULL);
942 profile_ = profile;
943
944 if (!had_profile && profile)
945 CheckBrailleState();
946 else
947 UpdateBrailleImeState();
948 UpdateLargeCursorFromPref();
949 UpdateStickyKeysFromPref();
950 UpdateSpokenFeedbackFromPref();
951 UpdateHighContrastFromPref();
952 UpdateAutoclickFromPref();
953 UpdateAutoclickDelayFromPref();
954 UpdateVirtualKeyboardFromPref();
955 }
956
ActiveUserChanged(const std::string & user_id)957 void AccessibilityManager::ActiveUserChanged(const std::string& user_id) {
958 SetProfile(ProfileManager::GetActiveUserProfile());
959 }
960
SetProfileForTest(Profile * profile)961 void AccessibilityManager::SetProfileForTest(Profile* profile) {
962 SetProfile(profile);
963 }
964
SetBrailleControllerForTest(BrailleController * controller)965 void AccessibilityManager::SetBrailleControllerForTest(
966 BrailleController* controller) {
967 g_braille_controller_for_test = controller;
968 }
969
EnableSystemSounds(bool system_sounds_enabled)970 void AccessibilityManager::EnableSystemSounds(bool system_sounds_enabled) {
971 system_sounds_enabled_ = system_sounds_enabled;
972 }
973
PlayShutdownSound()974 base::TimeDelta AccessibilityManager::PlayShutdownSound() {
975 if (!system_sounds_enabled_)
976 return base::TimeDelta();
977 system_sounds_enabled_ = false;
978 #if !defined(USE_ATHENA)
979 if (!ash::PlaySystemSoundIfSpokenFeedback(SOUND_SHUTDOWN))
980 return base::TimeDelta();
981 #endif
982 return media::SoundsManager::Get()->GetDuration(SOUND_SHUTDOWN);
983 }
984
InjectChromeVox(RenderViewHost * render_view_host)985 void AccessibilityManager::InjectChromeVox(RenderViewHost* render_view_host) {
986 LoadChromeVoxExtension(profile_, render_view_host, base::Closure());
987 }
988
989 scoped_ptr<AccessibilityStatusSubscription>
RegisterCallback(const AccessibilityStatusCallback & cb)990 AccessibilityManager::RegisterCallback(
991 const AccessibilityStatusCallback& cb) {
992 return callback_list_.Add(cb);
993 }
994
NotifyAccessibilityStatusChanged(AccessibilityStatusEventDetails & details)995 void AccessibilityManager::NotifyAccessibilityStatusChanged(
996 AccessibilityStatusEventDetails& details) {
997 callback_list_.Notify(details);
998 }
999
UpdateChromeOSAccessibilityHistograms()1000 void AccessibilityManager::UpdateChromeOSAccessibilityHistograms() {
1001 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosSpokenFeedback",
1002 IsSpokenFeedbackEnabled());
1003 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosHighContrast",
1004 IsHighContrastEnabled());
1005 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosVirtualKeyboard",
1006 IsVirtualKeyboardEnabled());
1007 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosStickyKeys", IsStickyKeysEnabled());
1008 if (MagnificationManager::Get()) {
1009 uint32 type = MagnificationManager::Get()->IsMagnifierEnabled() ?
1010 MagnificationManager::Get()->GetMagnifierType() : 0;
1011 // '0' means magnifier is disabled.
1012 UMA_HISTOGRAM_ENUMERATION("Accessibility.CrosScreenMagnifier",
1013 type,
1014 ash::kMaxMagnifierType + 1);
1015 }
1016 if (profile_) {
1017 const PrefService* const prefs = profile_->GetPrefs();
1018 UMA_HISTOGRAM_BOOLEAN(
1019 "Accessibility.CrosLargeCursor",
1020 prefs->GetBoolean(prefs::kAccessibilityLargeCursorEnabled));
1021 UMA_HISTOGRAM_BOOLEAN(
1022 "Accessibility.CrosAlwaysShowA11yMenu",
1023 prefs->GetBoolean(prefs::kShouldAlwaysShowAccessibilityMenu));
1024
1025 bool autoclick_enabled =
1026 prefs->GetBoolean(prefs::kAccessibilityAutoclickEnabled);
1027 UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosAutoclick", autoclick_enabled);
1028 if (autoclick_enabled) {
1029 // We only want to log the autoclick delay if the user has actually
1030 // enabled autoclick.
1031 UMA_HISTOGRAM_CUSTOM_TIMES(
1032 "Accessibility.CrosAutoclickDelay",
1033 base::TimeDelta::FromMilliseconds(
1034 prefs->GetInteger(prefs::kAccessibilityAutoclickDelayMs)),
1035 base::TimeDelta::FromMilliseconds(1),
1036 base::TimeDelta::FromMilliseconds(3000),
1037 50);
1038 }
1039 }
1040 }
1041
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)1042 void AccessibilityManager::Observe(
1043 int type,
1044 const content::NotificationSource& source,
1045 const content::NotificationDetails& details) {
1046 switch (type) {
1047 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
1048 // Update |profile_| when entering the login screen.
1049 Profile* profile = ProfileManager::GetActiveUserProfile();
1050 if (ProfileHelper::IsSigninProfile(profile))
1051 SetProfile(profile);
1052 break;
1053 }
1054 case chrome::NOTIFICATION_SESSION_STARTED:
1055 // Update |profile_| when entering a session.
1056 SetProfile(ProfileManager::GetActiveUserProfile());
1057
1058 // Ensure ChromeVox makes announcements at the start of new sessions.
1059 should_speak_chrome_vox_announcements_on_user_screen_ = true;
1060
1061 // Add a session state observer to be able to monitor session changes.
1062 if (!session_state_observer_.get() && ash::Shell::HasInstance())
1063 session_state_observer_.reset(
1064 new ash::ScopedSessionStateObserver(this));
1065 break;
1066 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
1067 // Update |profile_| when exiting a session or shutting down.
1068 Profile* profile = content::Source<Profile>(source).ptr();
1069 if (profile_ == profile)
1070 SetProfile(NULL);
1071 break;
1072 }
1073 case chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED: {
1074 bool is_screen_locked = *content::Details<bool>(details).ptr();
1075 if (spoken_feedback_enabled_) {
1076 if (is_screen_locked)
1077 LoadChromeVoxToLockScreen(base::Closure());
1078 // If spoken feedback was enabled, make sure it is also enabled on
1079 // the user screen.
1080 // The status tray gets verbalized by user screen ChromeVox, so we need
1081 // to load it on the user screen even if the screen is locked.
1082 LoadChromeVoxToUserScreen(base::Closure());
1083 }
1084 break;
1085 }
1086 }
1087 }
1088
OnBrailleDisplayStateChanged(const DisplayState & display_state)1089 void AccessibilityManager::OnBrailleDisplayStateChanged(
1090 const DisplayState& display_state) {
1091 braille_display_connected_ = display_state.available;
1092 if (braille_display_connected_) {
1093 EnableSpokenFeedback(true, ash::A11Y_NOTIFICATION_SHOW);
1094 }
1095 UpdateBrailleImeState();
1096
1097 AccessibilityStatusEventDetails details(
1098 ACCESSIBILITY_BRAILLE_DISPLAY_CONNECTION_STATE_CHANGED,
1099 braille_display_connected_,
1100 ash::A11Y_NOTIFICATION_SHOW);
1101 NotifyAccessibilityStatusChanged(details);
1102 }
1103
OnBrailleKeyEvent(const KeyEvent & event)1104 void AccessibilityManager::OnBrailleKeyEvent(const KeyEvent& event) {
1105 // Ensure the braille IME is active on braille keyboard (dots) input.
1106 if ((event.command ==
1107 extensions::api::braille_display_private::KEY_COMMAND_DOTS) &&
1108 !braille_ime_current_) {
1109 input_method::InputMethodManager::Get()
1110 ->GetActiveIMEState()
1111 ->ChangeInputMethod(extension_misc::kBrailleImeEngineId,
1112 false /* show_message */);
1113 }
1114 }
1115
PostLoadChromeVox(Profile * profile)1116 void AccessibilityManager::PostLoadChromeVox(Profile* profile) {
1117 // Do any setup work needed immediately after ChromeVox actually loads.
1118 if (system_sounds_enabled_)
1119 ash::PlaySystemSoundAlways(SOUND_SPOKEN_FEEDBACK_ENABLED);
1120
1121 ExtensionAccessibilityEventRouter::GetInstance()->
1122 OnChromeVoxLoadStateChanged(profile_,
1123 IsSpokenFeedbackEnabled(),
1124 chrome_vox_loaded_on_lock_screen_ ||
1125 should_speak_chrome_vox_announcements_on_user_screen_);
1126
1127 should_speak_chrome_vox_announcements_on_user_screen_ =
1128 chrome_vox_loaded_on_lock_screen_;
1129 }
1130
PostUnloadChromeVox(Profile * profile)1131 void AccessibilityManager::PostUnloadChromeVox(Profile* profile) {
1132 // Do any teardown work needed immediately after ChromeVox actually unloads.
1133 if (system_sounds_enabled_)
1134 ash::PlaySystemSoundAlways(SOUND_SPOKEN_FEEDBACK_DISABLED);
1135 // Clear the accessibility focus ring.
1136 AccessibilityFocusRingController::GetInstance()->SetFocusRing(
1137 std::vector<gfx::Rect>());
1138 }
1139
1140 } // namespace chromeos
1141