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/high_contrast/high_contrast_controller.h"
      8 #include "ash/shell.h"
      9 #include "ash/system/tray/system_tray_notifier.h"
     10 #include "ash/wm/event_rewriter_event_filter.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/memory/singleton.h"
     13 #include "base/metrics/histogram.h"
     14 #include "base/prefs/pref_member.h"
     15 #include "base/prefs/pref_service.h"
     16 #include "base/values.h"
     17 #include "chrome/browser/accessibility/accessibility_extension_api.h"
     18 #include "chrome/browser/browser_process.h"
     19 #include "chrome/browser/chrome_notification_types.h"
     20 #include "chrome/browser/chromeos/accessibility/magnification_manager.h"
     21 #include "chrome/browser/chromeos/login/login_display_host.h"
     22 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
     23 #include "chrome/browser/chromeos/login/screen_locker.h"
     24 #include "chrome/browser/chromeos/login/webui_login_view.h"
     25 #include "chrome/browser/chromeos/profiles/profile_helper.h"
     26 #include "chrome/browser/extensions/component_loader.h"
     27 #include "chrome/browser/extensions/extension_service.h"
     28 #include "chrome/browser/extensions/extension_system.h"
     29 #include "chrome/browser/profiles/profile.h"
     30 #include "chrome/browser/profiles/profile_manager.h"
     31 #include "chrome/browser/speech/tts_controller.h"
     32 #include "chrome/common/extensions/extension.h"
     33 #include "chrome/common/extensions/extension_messages.h"
     34 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
     35 #include "chrome/common/pref_names.h"
     36 #include "chromeos/login/login_state.h"
     37 #include "content/public/browser/browser_accessibility_state.h"
     38 #include "content/public/browser/notification_details.h"
     39 #include "content/public/browser/notification_service.h"
     40 #include "content/public/browser/notification_source.h"
     41 #include "content/public/browser/render_process_host.h"
     42 #include "content/public/browser/render_view_host.h"
     43 #include "content/public/browser/web_contents.h"
     44 #include "content/public/browser/web_ui.h"
     45 #include "extensions/browser/file_reader.h"
     46 #include "extensions/common/extension_resource.h"
     47 #include "grit/browser_resources.h"
     48 #include "grit/generated_resources.h"
     49 #include "ui/base/l10n/l10n_util.h"
     50 #include "ui/base/resource/resource_bundle.h"
     51 
     52 using content::RenderViewHost;
     53 
     54 namespace chromeos {
     55 
     56 namespace {
     57 
     58 static chromeos::AccessibilityManager* g_accessibility_manager = NULL;
     59 
     60 // Helper class that directly loads an extension's content scripts into
     61 // all of the frames corresponding to a given RenderViewHost.
     62 class ContentScriptLoader {
     63  public:
     64   // Initialize the ContentScriptLoader with the ID of the extension
     65   // and the RenderViewHost where the scripts should be loaded.
     66   ContentScriptLoader(const std::string& extension_id,
     67                       int render_process_id,
     68                       int render_view_id)
     69       : extension_id_(extension_id),
     70         render_process_id_(render_process_id),
     71         render_view_id_(render_view_id) {}
     72 
     73   // Call this once with the ExtensionResource corresponding to each
     74   // content script to be loaded.
     75   void AppendScript(extensions::ExtensionResource resource) {
     76     resources_.push(resource);
     77   }
     78 
     79   // Fianlly, call this method once to fetch all of the resources and
     80   // load them. This method will delete this object when done.
     81   void Run() {
     82     if (resources_.empty()) {
     83       delete this;
     84       return;
     85     }
     86 
     87     extensions::ExtensionResource resource = resources_.front();
     88     resources_.pop();
     89     scoped_refptr<FileReader> reader(new FileReader(resource, base::Bind(
     90         &ContentScriptLoader::OnFileLoaded, base::Unretained(this))));
     91     reader->Start();
     92   }
     93 
     94  private:
     95   void OnFileLoaded(bool success, const std::string& data) {
     96     if (success) {
     97       ExtensionMsg_ExecuteCode_Params params;
     98       params.request_id = 0;
     99       params.extension_id = extension_id_;
    100       params.is_javascript = true;
    101       params.code = data;
    102       params.run_at = extensions::UserScript::DOCUMENT_IDLE;
    103       params.all_frames = true;
    104       params.in_main_world = false;
    105 
    106       RenderViewHost* render_view_host =
    107           RenderViewHost::FromID(render_process_id_, render_view_id_);
    108       if (render_view_host) {
    109         render_view_host->Send(new ExtensionMsg_ExecuteCode(
    110             render_view_host->GetRoutingID(), params));
    111       }
    112     }
    113     Run();
    114   }
    115 
    116   std::string extension_id_;
    117   int render_process_id_;
    118   int render_view_id_;
    119   std::queue<extensions::ExtensionResource> resources_;
    120 };
    121 
    122 void LoadChromeVoxExtension(Profile* profile, content::WebUI* login_web_ui) {
    123   ExtensionService* extension_service =
    124       extensions::ExtensionSystem::Get(profile)->extension_service();
    125   base::FilePath path = base::FilePath(extension_misc::kChromeVoxExtensionPath);
    126   std::string extension_id =
    127       extension_service->component_loader()->Add(IDR_CHROMEVOX_MANIFEST,
    128                                                  path);
    129   if (login_web_ui) {
    130     ExtensionService* extension_service =
    131         extensions::ExtensionSystem::Get(profile)->extension_service();
    132     const extensions::Extension* extension =
    133         extension_service->extensions()->GetByID(extension_id);
    134 
    135     RenderViewHost* render_view_host =
    136         login_web_ui->GetWebContents()->GetRenderViewHost();
    137     // Set a flag to tell ChromeVox that it's just been enabled,
    138     // so that it won't interrupt our speech feedback enabled message.
    139     ExtensionMsg_ExecuteCode_Params params;
    140     params.request_id = 0;
    141     params.extension_id = extension->id();
    142     params.is_javascript = true;
    143     params.code = "window.INJECTED_AFTER_LOAD = true;";
    144     params.run_at = extensions::UserScript::DOCUMENT_IDLE;
    145     params.all_frames = true;
    146     params.in_main_world = false;
    147     render_view_host->Send(new ExtensionMsg_ExecuteCode(
    148         render_view_host->GetRoutingID(), params));
    149 
    150     // Inject ChromeVox' content scripts.
    151     ContentScriptLoader* loader = new ContentScriptLoader(
    152         extension->id(), render_view_host->GetProcess()->GetID(),
    153         render_view_host->GetRoutingID());
    154 
    155     const extensions::UserScriptList& content_scripts =
    156         extensions::ContentScriptsInfo::GetContentScripts(extension);
    157     for (size_t i = 0; i < content_scripts.size(); i++) {
    158       const extensions::UserScript& script = content_scripts[i];
    159       for (size_t j = 0; j < script.js_scripts().size(); ++j) {
    160         const extensions::UserScript::File &file = script.js_scripts()[j];
    161         extensions::ExtensionResource resource = extension->GetResource(
    162             file.relative_path());
    163         loader->AppendScript(resource);
    164       }
    165     }
    166     loader->Run();  // It cleans itself up when done.
    167   }
    168   DLOG(INFO) << "ChromeVox was Loaded.";
    169 }
    170 
    171 void UnloadChromeVoxExtension(Profile* profile) {
    172   ExtensionService* extension_service =
    173       extensions::ExtensionSystem::Get(profile)->extension_service();
    174   base::FilePath path = base::FilePath(extension_misc::kChromeVoxExtensionPath);
    175   extension_service->component_loader()->Remove(path);
    176   DLOG(INFO) << "ChromeVox was Unloaded.";
    177 }
    178 
    179 }  // namespace
    180 
    181 ///////////////////////////////////////////////////////////////////////////////
    182 // AccessibilityStatusEventDetails
    183 
    184 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
    185     bool enabled,
    186     ash::AccessibilityNotificationVisibility notify)
    187   : enabled(enabled),
    188     magnifier_type(ash::kDefaultMagnifierType),
    189     notify(notify) {}
    190 
    191 AccessibilityStatusEventDetails::AccessibilityStatusEventDetails(
    192     bool enabled,
    193     ash::MagnifierType magnifier_type,
    194     ash::AccessibilityNotificationVisibility notify)
    195   : enabled(enabled),
    196     magnifier_type(magnifier_type),
    197     notify(notify) {}
    198 
    199 ///////////////////////////////////////////////////////////////////////////////
    200 //
    201 // AccessibilityManager::PrefHandler
    202 
    203 AccessibilityManager::PrefHandler::PrefHandler(const char* pref_path)
    204     : pref_path_(pref_path) {}
    205 
    206 AccessibilityManager::PrefHandler::~PrefHandler() {}
    207 
    208 void AccessibilityManager::PrefHandler::HandleProfileChanged(
    209     Profile* previous_profile, Profile* current_profile) {
    210   // Returns if the current profile is null.
    211   if (!current_profile)
    212     return;
    213 
    214   // If the user set a pref value on the login screen and is now starting a
    215   // session with a new profile, copy the pref value to the profile.
    216   if ((previous_profile &&
    217        ProfileHelper::IsSigninProfile(previous_profile) &&
    218        current_profile->IsNewProfile() &&
    219        !ProfileHelper::IsSigninProfile(current_profile)) ||
    220       // Special case for Guest mode:
    221       // Guest mode launches a guest-mode browser process before session starts,
    222       // so the previous profile is null.
    223       (!previous_profile &&
    224        current_profile->IsGuestSession())) {
    225     // Returns if the pref has not been set by the user.
    226     const PrefService::Preference* pref = ProfileHelper::GetSigninProfile()->
    227         GetPrefs()->FindPreference(pref_path_);
    228     if (!pref || !pref->IsUserControlled())
    229       return;
    230 
    231     // Copy the pref value from the signin screen.
    232     const base::Value* value_on_login = pref->GetValue();
    233     PrefService* user_prefs = current_profile->GetPrefs();
    234     user_prefs->Set(pref_path_, *value_on_login);
    235   }
    236 }
    237 
    238 ///////////////////////////////////////////////////////////////////////////////
    239 //
    240 // AccessibilityManager
    241 
    242 // static
    243 void AccessibilityManager::Initialize() {
    244   CHECK(g_accessibility_manager == NULL);
    245   g_accessibility_manager = new AccessibilityManager();
    246 }
    247 
    248 // static
    249 void AccessibilityManager::Shutdown() {
    250   CHECK(g_accessibility_manager);
    251   delete g_accessibility_manager;
    252   g_accessibility_manager = NULL;
    253 }
    254 
    255 // static
    256 AccessibilityManager* AccessibilityManager::Get() {
    257   return g_accessibility_manager;
    258 }
    259 
    260 AccessibilityManager::AccessibilityManager()
    261     : profile_(NULL),
    262       chrome_vox_loaded_on_lock_screen_(false),
    263       chrome_vox_loaded_on_user_screen_(false),
    264       large_cursor_pref_handler_(prefs::kLargeCursorEnabled),
    265       spoken_feedback_pref_handler_(prefs::kSpokenFeedbackEnabled),
    266       high_contrast_pref_handler_(prefs::kHighContrastEnabled),
    267       large_cursor_enabled_(false),
    268       sticky_keys_enabled_(false),
    269       spoken_feedback_enabled_(false),
    270       high_contrast_enabled_(false),
    271       spoken_feedback_notification_(ash::A11Y_NOTIFICATION_NONE) {
    272 
    273   notification_registrar_.Add(this,
    274                               chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
    275                               content::NotificationService::AllSources());
    276   notification_registrar_.Add(this,
    277                               chrome::NOTIFICATION_SESSION_STARTED,
    278                               content::NotificationService::AllSources());
    279   notification_registrar_.Add(this,
    280                               chrome::NOTIFICATION_PROFILE_DESTROYED,
    281                               content::NotificationService::AllSources());
    282   notification_registrar_.Add(this,
    283                               chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED,
    284                               content::NotificationService::AllSources());
    285 }
    286 
    287 AccessibilityManager::~AccessibilityManager() {
    288   CHECK(this == g_accessibility_manager);
    289 }
    290 
    291 void AccessibilityManager::EnableLargeCursor(bool enabled) {
    292   if (!profile_)
    293     return;
    294 
    295   PrefService* pref_service = profile_->GetPrefs();
    296   pref_service->SetBoolean(prefs::kLargeCursorEnabled, enabled);
    297   pref_service->CommitPendingWrite();
    298 }
    299 
    300 void AccessibilityManager::UpdateLargeCursorFromPref() {
    301   if (!profile_)
    302     return;
    303 
    304   const bool enabled =
    305       profile_->GetPrefs()->GetBoolean(prefs::kLargeCursorEnabled);
    306 
    307   if (large_cursor_enabled_ == enabled)
    308     return;
    309 
    310   large_cursor_enabled_ = enabled;
    311 
    312   AccessibilityStatusEventDetails details(enabled, ash::A11Y_NOTIFICATION_NONE);
    313   content::NotificationService::current()->Notify(
    314       chrome::NOTIFICATION_CROS_ACCESSIBILITY_TOGGLE_LARGE_CURSOR,
    315       content::NotificationService::AllSources(),
    316       content::Details<AccessibilityStatusEventDetails>(&details));
    317 
    318 #if defined(USE_ASH)
    319   // Large cursor is implemented only in ash.
    320   ash::Shell::GetInstance()->cursor_manager()->SetScale(enabled ? 2.0 : 1.0);
    321 #endif
    322 }
    323 
    324 bool AccessibilityManager::IsLargeCursorEnabled() {
    325   return large_cursor_enabled_;
    326 }
    327 
    328 void AccessibilityManager::EnableStickyKeys(bool enabled) {
    329   if (!profile_)
    330     return;
    331   PrefService* pref_service = profile_->GetPrefs();
    332   pref_service->SetBoolean(prefs::kStickyKeysEnabled, enabled);
    333   pref_service->CommitPendingWrite();
    334 }
    335 
    336 bool AccessibilityManager::IsStickyKeysEnabled() {
    337   return sticky_keys_enabled_;
    338 }
    339 
    340 void AccessibilityManager::UpdateStickyKeysFromPref() {
    341   if (!profile_)
    342     return;
    343 
    344   const bool enabled =
    345       profile_->GetPrefs()->GetBoolean(prefs::kStickyKeysEnabled);
    346 
    347   if (sticky_keys_enabled_ == enabled)
    348     return;
    349 
    350   sticky_keys_enabled_ = enabled;
    351 #if defined(USE_ASH)
    352   // Sticky keys is implemented only in ash.
    353   ash::Shell::GetInstance()->event_rewriter_filter()->EnableStickyKeys(enabled);
    354 #endif
    355 }
    356 
    357 void AccessibilityManager::EnableSpokenFeedback(
    358     bool enabled,
    359     ash::AccessibilityNotificationVisibility notify) {
    360   if (!profile_)
    361     return;
    362 
    363   spoken_feedback_notification_ = notify;
    364 
    365   PrefService* pref_service = profile_->GetPrefs();
    366   pref_service->SetBoolean(
    367       prefs::kSpokenFeedbackEnabled, enabled);
    368   pref_service->CommitPendingWrite();
    369 
    370   spoken_feedback_notification_ = ash::A11Y_NOTIFICATION_NONE;
    371 }
    372 
    373 void AccessibilityManager::UpdateSpokenFeedbackFromPref() {
    374   if (!profile_)
    375     return;
    376 
    377   const bool enabled =
    378       profile_->GetPrefs()->GetBoolean(prefs::kSpokenFeedbackEnabled);
    379 
    380   if (spoken_feedback_enabled_ == enabled)
    381     return;
    382 
    383   spoken_feedback_enabled_ = enabled;
    384 
    385   ExtensionAccessibilityEventRouter::GetInstance()->
    386       SetAccessibilityEnabled(enabled);
    387 
    388   AccessibilityStatusEventDetails details(enabled,
    389                                           spoken_feedback_notification_);
    390   content::NotificationService::current()->Notify(
    391       chrome::NOTIFICATION_CROS_ACCESSIBILITY_TOGGLE_SPOKEN_FEEDBACK,
    392       content::NotificationService::AllSources(),
    393       content::Details<AccessibilityStatusEventDetails>(&details));
    394 
    395   Speak(l10n_util::GetStringUTF8(
    396       enabled ? IDS_CHROMEOS_ACC_SPOKEN_FEEDBACK_ENABLED :
    397       IDS_CHROMEOS_ACC_SPOKEN_FEEDBACK_DISABLED).c_str());
    398 
    399   if (enabled)
    400     LoadChromeVox();
    401   else
    402     UnloadChromeVox();
    403 }
    404 
    405 void AccessibilityManager::LoadChromeVox() {
    406   ScreenLocker* screen_locker = ScreenLocker::default_screen_locker();
    407   if (screen_locker && screen_locker->locked()) {
    408     // If on the lock screen, loads ChromeVox only to the lock screen as for
    409     // now. On unlock, it will be loaded to the user screen.
    410     // (see. AccessibilityManager::Observe())
    411     LoadChromeVoxToLockScreen();
    412     return;
    413   }
    414 
    415   LoadChromeVoxToUserScreen();
    416 }
    417 
    418 void AccessibilityManager::LoadChromeVoxToUserScreen() {
    419   if (chrome_vox_loaded_on_user_screen_)
    420     return;
    421 
    422   // Determine whether an OOBE screen is currently being shown. If so,
    423   // ChromeVox will be injected directly into that screen.
    424   content::WebUI* login_web_ui = NULL;
    425 
    426   if (ProfileHelper::IsSigninProfile(profile_)) {
    427     LoginDisplayHost* login_display_host = LoginDisplayHostImpl::default_host();
    428     if (login_display_host) {
    429       WebUILoginView* web_ui_login_view =
    430           login_display_host->GetWebUILoginView();
    431       if (web_ui_login_view)
    432         login_web_ui = web_ui_login_view->GetWebUI();
    433     }
    434   }
    435 
    436   LoadChromeVoxExtension(profile_, login_web_ui);
    437   chrome_vox_loaded_on_user_screen_ = true;
    438 }
    439 
    440 void AccessibilityManager::LoadChromeVoxToLockScreen() {
    441   if (chrome_vox_loaded_on_lock_screen_)
    442     return;
    443 
    444   ScreenLocker* screen_locker = ScreenLocker::default_screen_locker();
    445   if (screen_locker && screen_locker->locked()) {
    446     content::WebUI* lock_web_ui = screen_locker->GetAssociatedWebUI();
    447     if (lock_web_ui) {
    448       Profile* profile = Profile::FromWebUI(lock_web_ui);
    449       LoadChromeVoxExtension(profile, lock_web_ui);
    450       chrome_vox_loaded_on_lock_screen_ = true;
    451     }
    452   }
    453 }
    454 
    455 void AccessibilityManager::UnloadChromeVox() {
    456   if (chrome_vox_loaded_on_lock_screen_)
    457     UnloadChromeVoxFromLockScreen();
    458 
    459   if (chrome_vox_loaded_on_user_screen_) {
    460     UnloadChromeVoxExtension(profile_);
    461     chrome_vox_loaded_on_user_screen_ = false;
    462   }
    463 }
    464 
    465 void AccessibilityManager::UnloadChromeVoxFromLockScreen() {
    466   // Lock screen uses the signin progile.
    467   Profile* signin_profile = ProfileHelper::GetSigninProfile();
    468   UnloadChromeVoxExtension(signin_profile);
    469   chrome_vox_loaded_on_lock_screen_ = false;
    470 }
    471 
    472 bool AccessibilityManager::IsSpokenFeedbackEnabled() {
    473   return spoken_feedback_enabled_;
    474 }
    475 
    476 void AccessibilityManager::ToggleSpokenFeedback(
    477     ash::AccessibilityNotificationVisibility notify) {
    478   EnableSpokenFeedback(!IsSpokenFeedbackEnabled(), notify);
    479 }
    480 
    481 void AccessibilityManager::Speak(const std::string& text) {
    482   UtteranceContinuousParameters params;
    483 
    484   Utterance* utterance = new Utterance(profile_);
    485   utterance->set_text(text);
    486   utterance->set_lang(g_browser_process->GetApplicationLocale());
    487   utterance->set_continuous_parameters(params);
    488   utterance->set_can_enqueue(false);
    489   utterance->set_options(new DictionaryValue());
    490 
    491   TtsController* controller = TtsController::GetInstance();
    492   controller->SpeakOrEnqueue(utterance);
    493 }
    494 
    495 void AccessibilityManager::MaybeSpeak(const std::string& text) {
    496   if (IsSpokenFeedbackEnabled())
    497     Speak(text);
    498 }
    499 
    500 void AccessibilityManager::EnableHighContrast(bool enabled) {
    501   if (!profile_)
    502     return;
    503 
    504   PrefService* pref_service = profile_->GetPrefs();
    505   pref_service->SetBoolean(prefs::kHighContrastEnabled, enabled);
    506   pref_service->CommitPendingWrite();
    507 }
    508 
    509 void AccessibilityManager::UpdateHighContrastFromPref() {
    510   if (!profile_)
    511     return;
    512 
    513   const bool enabled =
    514       profile_->GetPrefs()->GetBoolean(prefs::kHighContrastEnabled);
    515 
    516   if (high_contrast_enabled_ == enabled)
    517     return;
    518 
    519   high_contrast_enabled_ = enabled;
    520 
    521   AccessibilityStatusEventDetails detail(enabled, ash::A11Y_NOTIFICATION_NONE);
    522   content::NotificationService::current()->Notify(
    523       chrome::NOTIFICATION_CROS_ACCESSIBILITY_TOGGLE_HIGH_CONTRAST_MODE,
    524       content::NotificationService::AllSources(),
    525       content::Details<AccessibilityStatusEventDetails>(&detail));
    526 
    527 #if defined(USE_ASH)
    528   ash::Shell::GetInstance()->high_contrast_controller()->SetEnabled(enabled);
    529 #endif
    530 }
    531 
    532 void AccessibilityManager::LocalePrefChanged() {
    533   if (!profile_)
    534     return;
    535 
    536   if (!IsSpokenFeedbackEnabled())
    537     return;
    538 
    539   // If the system locale changes and spoken feedback is enabled,
    540   // reload ChromeVox so that it switches its internal translations
    541   // to the new language.
    542   EnableSpokenFeedback(false, ash::A11Y_NOTIFICATION_NONE);
    543   EnableSpokenFeedback(true, ash::A11Y_NOTIFICATION_NONE);
    544 }
    545 
    546 bool AccessibilityManager::IsHighContrastEnabled() {
    547   return high_contrast_enabled_;
    548 }
    549 
    550 void AccessibilityManager::SetProfile(Profile* profile) {
    551   pref_change_registrar_.reset();
    552   local_state_pref_change_registrar_.reset();
    553 
    554   if (profile) {
    555     // TODO(yoshiki): Move following code to PrefHandler.
    556     pref_change_registrar_.reset(new PrefChangeRegistrar);
    557     pref_change_registrar_->Init(profile->GetPrefs());
    558     pref_change_registrar_->Add(
    559         prefs::kLargeCursorEnabled,
    560         base::Bind(&AccessibilityManager::UpdateLargeCursorFromPref,
    561                    base::Unretained(this)));
    562     pref_change_registrar_->Add(
    563         prefs::kStickyKeysEnabled,
    564         base::Bind(&AccessibilityManager::UpdateStickyKeysFromPref,
    565                    base::Unretained(this)));
    566     pref_change_registrar_->Add(
    567         prefs::kSpokenFeedbackEnabled,
    568         base::Bind(&AccessibilityManager::UpdateSpokenFeedbackFromPref,
    569                    base::Unretained(this)));
    570     pref_change_registrar_->Add(
    571         prefs::kHighContrastEnabled,
    572         base::Bind(&AccessibilityManager::UpdateHighContrastFromPref,
    573                    base::Unretained(this)));
    574 
    575     local_state_pref_change_registrar_.reset(new PrefChangeRegistrar);
    576     local_state_pref_change_registrar_->Init(g_browser_process->local_state());
    577     local_state_pref_change_registrar_->Add(
    578         prefs::kApplicationLocale,
    579         base::Bind(&AccessibilityManager::LocalePrefChanged,
    580                    base::Unretained(this)));
    581 
    582     content::BrowserAccessibilityState::GetInstance()->AddHistogramCallback(
    583         base::Bind(
    584             &AccessibilityManager::UpdateChromeOSAccessibilityHistograms,
    585             base::Unretained(this)));
    586   }
    587 
    588   large_cursor_pref_handler_.HandleProfileChanged(profile_, profile);
    589   spoken_feedback_pref_handler_.HandleProfileChanged(profile_, profile);
    590   high_contrast_pref_handler_.HandleProfileChanged(profile_, profile);
    591 
    592   profile_ = profile;
    593   UpdateLargeCursorFromPref();
    594   UpdateStickyKeysFromPref();
    595   UpdateSpokenFeedbackFromPref();
    596   UpdateHighContrastFromPref();
    597 }
    598 
    599 void AccessibilityManager::SetProfileForTest(Profile* profile) {
    600   SetProfile(profile);
    601 }
    602 
    603 void AccessibilityManager::UpdateChromeOSAccessibilityHistograms() {
    604   UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosSpokenFeedback",
    605                         IsSpokenFeedbackEnabled());
    606   UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosHighContrast",
    607                         IsHighContrastEnabled());
    608   UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosVirtualKeyboard",
    609                         accessibility::IsVirtualKeyboardEnabled());
    610   if (MagnificationManager::Get()) {
    611     uint32 type = MagnificationManager::Get()->IsMagnifierEnabled() ?
    612                       MagnificationManager::Get()->GetMagnifierType() : 0;
    613     // '0' means magnifier is disabled.
    614     UMA_HISTOGRAM_ENUMERATION("Accessibility.CrosScreenMagnifier",
    615                               type,
    616                               ash::kMaxMagnifierType + 1);
    617   }
    618   if (profile_) {
    619     const PrefService* const prefs = profile_->GetPrefs();
    620     UMA_HISTOGRAM_BOOLEAN("Accessibility.CrosLargeCursor",
    621                           prefs->GetBoolean(prefs::kLargeCursorEnabled));
    622     UMA_HISTOGRAM_BOOLEAN(
    623         "Accessibility.CrosAlwaysShowA11yMenu",
    624         prefs->GetBoolean(prefs::kShouldAlwaysShowAccessibilityMenu));
    625   }
    626 }
    627 
    628 void AccessibilityManager::Observe(
    629     int type,
    630     const content::NotificationSource& source,
    631     const content::NotificationDetails& details) {
    632   switch (type) {
    633     case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
    634       // Update |profile_| when entering the login screen.
    635       Profile* profile = ProfileManager::GetDefaultProfile();
    636       if (ProfileHelper::IsSigninProfile(profile))
    637         SetProfile(profile);
    638       break;
    639     }
    640     case chrome::NOTIFICATION_SESSION_STARTED:
    641       // Update |profile_| when entering a session.
    642       SetProfile(ProfileManager::GetDefaultProfile());
    643       break;
    644     case chrome::NOTIFICATION_PROFILE_DESTROYED: {
    645       // Update |profile_| when exiting a session or shutting down.
    646       Profile* profile = content::Source<Profile>(source).ptr();
    647       if (profile_ == profile)
    648         SetProfile(NULL);
    649       break;
    650     }
    651     case chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED: {
    652       bool is_screen_locked = *content::Details<bool>(details).ptr();
    653       if (is_screen_locked) {
    654         if (spoken_feedback_enabled_)
    655           LoadChromeVoxToLockScreen();
    656       } else {
    657         UnloadChromeVoxFromLockScreen();
    658 
    659         if (spoken_feedback_enabled_)
    660           LoadChromeVoxToUserScreen();
    661       }
    662     }
    663   }
    664 }
    665 
    666 }  // namespace chromeos
    667