Home | History | Annotate | Download | only in chromeos
      1 // Copyright (c) 2012 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/locale_change_guard.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "ash/shell.h"
     10 #include "ash/system/tray/system_tray.h"
     11 #include "ash/system/tray/system_tray_notifier.h"
     12 #include "base/bind.h"
     13 #include "base/prefs/pref_service.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 #include "chrome/app/chrome_command_ids.h"
     16 #include "chrome/browser/browser_process.h"
     17 #include "chrome/browser/chrome_notification_types.h"
     18 #include "chrome/browser/chromeos/settings/device_settings_service.h"
     19 #include "chrome/browser/lifetime/application_lifetime.h"
     20 #include "chrome/browser/profiles/profile.h"
     21 #include "chrome/browser/ui/browser.h"
     22 #include "chrome/browser/ui/browser_commands.h"
     23 #include "chrome/browser/ui/host_desktop.h"
     24 #include "chrome/common/pref_names.h"
     25 #include "chrome/grit/generated_resources.h"
     26 #include "content/public/browser/notification_service.h"
     27 #include "content/public/browser/notification_source.h"
     28 #include "content/public/browser/user_metrics.h"
     29 #include "content/public/browser/web_contents.h"
     30 #include "ui/base/l10n/l10n_util.h"
     31 
     32 using base::UserMetricsAction;
     33 using content::WebContents;
     34 
     35 namespace chromeos {
     36 
     37 namespace {
     38 
     39 // This is the list of languages that do not require user notification when
     40 // locale is switched automatically between regions within the same language.
     41 //
     42 // New language in kAcceptLanguageList should be added either here or to
     43 // to the exception list in unit test.
     44 const char* const kSkipShowNotificationLanguages[4] = {"en", "de", "fr", "it"};
     45 
     46 }  // anonymous namespace
     47 
     48 LocaleChangeGuard::LocaleChangeGuard(Profile* profile)
     49     : profile_(profile),
     50       reverted_(false),
     51       session_started_(false),
     52       main_frame_loaded_(false) {
     53   DCHECK(profile_);
     54   registrar_.Add(this, chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED,
     55                  content::NotificationService::AllSources());
     56 }
     57 
     58 LocaleChangeGuard::~LocaleChangeGuard() {}
     59 
     60 void LocaleChangeGuard::OnLogin() {
     61   registrar_.Add(this, chrome::NOTIFICATION_SESSION_STARTED,
     62                  content::NotificationService::AllSources());
     63   registrar_.Add(this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
     64                  content::NotificationService::AllBrowserContextsAndSources());
     65 }
     66 
     67 void LocaleChangeGuard::RevertLocaleChange() {
     68   if (profile_ == NULL ||
     69       from_locale_.empty() ||
     70       to_locale_.empty()) {
     71     NOTREACHED();
     72     return;
     73   }
     74   if (reverted_)
     75     return;
     76   reverted_ = true;
     77   content::RecordAction(UserMetricsAction("LanguageChange_Revert"));
     78   profile_->ChangeAppLocale(
     79       from_locale_, Profile::APP_LOCALE_CHANGED_VIA_REVERT);
     80   chrome::AttemptUserExit();
     81 }
     82 
     83 void LocaleChangeGuard::RevertLocaleChangeCallback(
     84     const base::ListValue* list) {
     85   RevertLocaleChange();
     86 }
     87 
     88 void LocaleChangeGuard::Observe(int type,
     89                                 const content::NotificationSource& source,
     90                                 const content::NotificationDetails& details) {
     91   if (profile_ == NULL) {
     92     NOTREACHED();
     93     return;
     94   }
     95   switch (type) {
     96     case chrome::NOTIFICATION_SESSION_STARTED: {
     97       session_started_ = true;
     98       registrar_.Remove(this, chrome::NOTIFICATION_SESSION_STARTED,
     99                         content::NotificationService::AllSources());
    100       if (main_frame_loaded_)
    101         Check();
    102       break;
    103     }
    104     case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: {
    105       if (profile_ ==
    106           content::Source<WebContents>(source)->GetBrowserContext()) {
    107         main_frame_loaded_ = true;
    108         // We need to perform locale change check only once, so unsubscribe.
    109         registrar_.Remove(this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
    110                           content::NotificationService::AllSources());
    111         if (session_started_)
    112           Check();
    113       }
    114       break;
    115     }
    116     case chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED: {
    117       if (DeviceSettingsService::Get()->HasPrivateOwnerKey()) {
    118         PrefService* local_state = g_browser_process->local_state();
    119         if (local_state) {
    120           PrefService* prefs = profile_->GetPrefs();
    121           if (prefs == NULL) {
    122             NOTREACHED();
    123             return;
    124           }
    125           std::string owner_locale =
    126               prefs->GetString(prefs::kApplicationLocale);
    127           if (!owner_locale.empty())
    128             local_state->SetString(prefs::kOwnerLocale, owner_locale);
    129         }
    130       }
    131       break;
    132     }
    133     default: {
    134       NOTREACHED();
    135       break;
    136     }
    137   }
    138 }
    139 
    140 void LocaleChangeGuard::Check() {
    141   std::string cur_locale = g_browser_process->GetApplicationLocale();
    142   if (cur_locale.empty()) {
    143     NOTREACHED();
    144     return;
    145   }
    146 
    147   PrefService* prefs = profile_->GetPrefs();
    148   if (prefs == NULL) {
    149     NOTREACHED();
    150     return;
    151   }
    152 
    153   std::string to_locale = prefs->GetString(prefs::kApplicationLocale);
    154   if (to_locale != cur_locale) {
    155     // This conditional branch can occur in cases like:
    156     // (1) kApplicationLocale preference was modified by synchronization;
    157     // (2) kApplicationLocale is managed by policy.
    158     return;
    159   }
    160 
    161   std::string from_locale = prefs->GetString(prefs::kApplicationLocaleBackup);
    162   if (from_locale.empty() || from_locale == to_locale)
    163     return;  // No locale change was detected, just exit.
    164 
    165   if (prefs->GetString(prefs::kApplicationLocaleAccepted) == to_locale)
    166     return;  // Already accepted.
    167 
    168   // Locale change detected.
    169   if (!ShouldShowLocaleChangeNotification(from_locale, to_locale))
    170     return;
    171 
    172   // Showing notification.
    173   if (from_locale_ != from_locale || to_locale_ != to_locale) {
    174     // Falling back to showing message in current locale.
    175     LOG(ERROR) <<
    176         "Showing locale change notification in current (not previous) language";
    177     PrepareChangingLocale(from_locale, to_locale);
    178   }
    179 
    180 #if !defined(USE_ATHENA)
    181   // TODO(dpolukhin): Support locale change, crbug.com/411884.
    182   ash::Shell::GetInstance()->system_tray_notifier()->NotifyLocaleChanged(
    183       this, cur_locale, from_locale_, to_locale_);
    184 #endif
    185 }
    186 
    187 void LocaleChangeGuard::AcceptLocaleChange() {
    188   if (profile_ == NULL ||
    189       from_locale_.empty() ||
    190       to_locale_.empty()) {
    191     NOTREACHED();
    192     return;
    193   }
    194 
    195   // Check whether locale has been reverted or changed.
    196   // If not: mark current locale as accepted.
    197   if (reverted_)
    198     return;
    199   PrefService* prefs = profile_->GetPrefs();
    200   if (prefs == NULL) {
    201     NOTREACHED();
    202     return;
    203   }
    204   if (prefs->GetString(prefs::kApplicationLocale) != to_locale_)
    205     return;
    206   content::RecordAction(UserMetricsAction("LanguageChange_Accept"));
    207   prefs->SetString(prefs::kApplicationLocaleBackup, to_locale_);
    208   prefs->SetString(prefs::kApplicationLocaleAccepted, to_locale_);
    209 }
    210 
    211 void LocaleChangeGuard::PrepareChangingLocale(
    212     const std::string& from_locale, const std::string& to_locale) {
    213   std::string cur_locale = g_browser_process->GetApplicationLocale();
    214   if (!from_locale.empty())
    215     from_locale_ = from_locale;
    216   if (!to_locale.empty())
    217     to_locale_ = to_locale;
    218 
    219   if (!from_locale_.empty() && !to_locale_.empty()) {
    220     base::string16 from = l10n_util::GetDisplayNameForLocale(
    221         from_locale_, cur_locale, true);
    222     base::string16 to = l10n_util::GetDisplayNameForLocale(
    223         to_locale_, cur_locale, true);
    224 
    225     title_text_ = l10n_util::GetStringUTF16(
    226         IDS_OPTIONS_SETTINGS_SECTION_TITLE_LANGUAGE);
    227     message_text_ = l10n_util::GetStringFUTF16(
    228         IDS_LOCALE_CHANGE_MESSAGE, from, to);
    229     revert_link_text_ = l10n_util::GetStringFUTF16(
    230         IDS_LOCALE_CHANGE_REVERT_MESSAGE, from);
    231   }
    232 }
    233 
    234 // static
    235 bool LocaleChangeGuard::ShouldShowLocaleChangeNotification(
    236     const std::string& from_locale,
    237     const std::string& to_locale) {
    238   const std::string from_lang = l10n_util::GetLanguage(from_locale);
    239   const std::string to_lang = l10n_util::GetLanguage(to_locale);
    240 
    241   if (from_locale == to_locale)
    242     return false;
    243 
    244   if (from_lang != to_lang)
    245     return true;
    246 
    247   const char* const* begin = kSkipShowNotificationLanguages;
    248   const char* const* end = kSkipShowNotificationLanguages +
    249                            arraysize(kSkipShowNotificationLanguages);
    250 
    251   return std::find(begin, end, from_lang) == end;
    252 }
    253 
    254 // static
    255 const char* const*
    256 LocaleChangeGuard::GetSkipShowNotificationLanguagesForTesting() {
    257   return kSkipShowNotificationLanguages;
    258 }
    259 
    260 // static
    261 size_t LocaleChangeGuard::GetSkipShowNotificationLanguagesSizeForTesting() {
    262   return arraysize(kSkipShowNotificationLanguages);
    263 }
    264 
    265 }  // namespace chromeos
    266