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