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