Home | History | Annotate | Download | only in accessibility
      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 
     84 BrailleController* GetBrailleController() {
     85   return g_braille_controller_for_test
     86       ? g_braille_controller_for_test
     87       : BrailleController::GetInstance();
     88 }
     89 
     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.
    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.
    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.
    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:
    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 
    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 
    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 
    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 
    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 
    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 
    269 AccessibilityManager::PrefHandler::PrefHandler(const char* pref_path)
    270     : pref_path_(pref_path) {}
    271 
    272 AccessibilityManager::PrefHandler::~PrefHandler() {}
    273 
    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
    309 void AccessibilityManager::Initialize() {
    310   CHECK(g_accessibility_manager == NULL);
    311   g_accessibility_manager = new AccessibilityManager();
    312 }
    313 
    314 // static
    315 void AccessibilityManager::Shutdown() {
    316   CHECK(g_accessibility_manager);
    317   delete g_accessibility_manager;
    318   g_accessibility_manager = NULL;
    319 }
    320 
    321 // static
    322 AccessibilityManager* AccessibilityManager::Get() {
    323   return g_accessibility_manager;
    324 }
    325 
    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 
    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 
    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 
    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 
    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 
    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 
    475 bool AccessibilityManager::IsIncognitoAllowed() {
    476   // Supervised users can't create incognito-mode windows.
    477   return !(user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser());
    478 }
    479 
    480 bool AccessibilityManager::IsLargeCursorEnabled() {
    481   return large_cursor_enabled_;
    482 }
    483 
    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 
    492 bool AccessibilityManager::IsStickyKeysEnabled() {
    493   return sticky_keys_enabled_;
    494 }
    495 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    644 bool AccessibilityManager::IsSpokenFeedbackEnabled() {
    645   return spoken_feedback_enabled_;
    646 }
    647 
    648 void AccessibilityManager::ToggleSpokenFeedback(
    649     ash::AccessibilityNotificationVisibility notify) {
    650   EnableSpokenFeedback(!IsSpokenFeedbackEnabled(), notify);
    651 }
    652 
    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 
    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 
    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 
    709 void AccessibilityManager::PlayEarcon(int sound_key) {
    710   DCHECK(sound_key < chromeos::SOUND_COUNT);
    711   ash::PlaySystemSoundIfSpokenFeedback(sound_key);
    712 }
    713 
    714 bool AccessibilityManager::IsHighContrastEnabled() {
    715   return high_contrast_enabled_;
    716 }
    717 
    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 
    727 bool AccessibilityManager::IsAutoclickEnabled() {
    728   return autoclick_enabled_;
    729 }
    730 
    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 
    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 
    753 int AccessibilityManager::GetAutoclickDelay() const {
    754   return autoclick_delay_ms_;
    755 }
    756 
    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 
    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 
    781 bool AccessibilityManager::IsVirtualKeyboardEnabled() {
    782   return virtual_keyboard_enabled_;
    783 }
    784 
    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 
    819 bool AccessibilityManager::IsBrailleDisplayConnected() const {
    820   return braille_display_connected_;
    821 }
    822 
    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 
    836 void AccessibilityManager::ReceiveBrailleDisplayState(
    837     scoped_ptr<extensions::api::braille_display_private::DisplayState> state) {
    838   OnBrailleDisplayStateChanged(*state);
    839 }
    840 
    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.
    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 
    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 
    957 void AccessibilityManager::ActiveUserChanged(const std::string& user_id) {
    958   SetProfile(ProfileManager::GetActiveUserProfile());
    959 }
    960 
    961 void AccessibilityManager::SetProfileForTest(Profile* profile) {
    962   SetProfile(profile);
    963 }
    964 
    965 void AccessibilityManager::SetBrailleControllerForTest(
    966     BrailleController* controller) {
    967   g_braille_controller_for_test = controller;
    968 }
    969 
    970 void AccessibilityManager::EnableSystemSounds(bool system_sounds_enabled) {
    971   system_sounds_enabled_ = system_sounds_enabled;
    972 }
    973 
    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 
    985 void AccessibilityManager::InjectChromeVox(RenderViewHost* render_view_host) {
    986   LoadChromeVoxExtension(profile_, render_view_host, base::Closure());
    987 }
    988 
    989 scoped_ptr<AccessibilityStatusSubscription>
    990     AccessibilityManager::RegisterCallback(
    991         const AccessibilityStatusCallback& cb) {
    992   return callback_list_.Add(cb);
    993 }
    994 
    995 void AccessibilityManager::NotifyAccessibilityStatusChanged(
    996     AccessibilityStatusEventDetails& details) {
    997   callback_list_.Notify(details);
    998 }
    999 
   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 
   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 
   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 
   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 
   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 
   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