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 "ash/shell.h" 8 #include "ash/system/tray/system_tray.h" 9 #include "ash/system/tray/system_tray_notifier.h" 10 #include "base/bind.h" 11 #include "base/prefs/pref_service.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "chrome/app/chrome_command_ids.h" 14 #include "chrome/browser/browser_process.h" 15 #include "chrome/browser/chrome_notification_types.h" 16 #include "chrome/browser/chromeos/settings/device_settings_service.h" 17 #include "chrome/browser/lifetime/application_lifetime.h" 18 #include "chrome/browser/notifications/notification_delegate.h" 19 #include "chrome/browser/profiles/profile.h" 20 #include "chrome/browser/ui/browser.h" 21 #include "chrome/browser/ui/browser_commands.h" 22 #include "chrome/browser/ui/host_desktop.h" 23 #include "chrome/common/pref_names.h" 24 #include "content/public/browser/notification_service.h" 25 #include "content/public/browser/notification_source.h" 26 #include "content/public/browser/user_metrics.h" 27 #include "content/public/browser/web_contents.h" 28 #include "grit/generated_resources.h" 29 #include "grit/theme_resources.h" 30 #include "ui/base/l10n/l10n_util.h" 31 32 using content::UserMetricsAction; 33 using content::WebContents; 34 35 namespace chromeos { 36 37 class LocaleChangeGuard::Delegate : public NotificationDelegate { 38 public: 39 explicit Delegate(chromeos::LocaleChangeGuard* master) : master_(master) {} 40 virtual void Close(bool by_user) OVERRIDE; 41 virtual void Display() OVERRIDE {} 42 virtual void Error() OVERRIDE {} 43 virtual void Click() OVERRIDE {} 44 virtual std::string id() const OVERRIDE; 45 virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE { 46 return NULL; 47 } 48 49 protected: 50 virtual ~Delegate() {} 51 52 private: 53 chromeos::LocaleChangeGuard* master_; 54 55 DISALLOW_COPY_AND_ASSIGN(Delegate); 56 }; 57 58 LocaleChangeGuard::LocaleChangeGuard(Profile* profile) 59 : profile_(profile), 60 reverted_(false), 61 session_started_(false), 62 main_frame_loaded_(false) { 63 DCHECK(profile_); 64 registrar_.Add(this, chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED, 65 content::NotificationService::AllSources()); 66 } 67 68 LocaleChangeGuard::~LocaleChangeGuard() {} 69 70 void LocaleChangeGuard::OnLogin() { 71 registrar_.Add(this, chrome::NOTIFICATION_SESSION_STARTED, 72 content::NotificationService::AllSources()); 73 registrar_.Add(this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, 74 content::NotificationService::AllBrowserContextsAndSources()); 75 } 76 77 void LocaleChangeGuard::RevertLocaleChange() { 78 if (profile_ == NULL || 79 from_locale_.empty() || 80 to_locale_.empty()) { 81 NOTREACHED(); 82 return; 83 } 84 if (reverted_) 85 return; 86 reverted_ = true; 87 content::RecordAction(UserMetricsAction("LanguageChange_Revert")); 88 profile_->ChangeAppLocale( 89 from_locale_, Profile::APP_LOCALE_CHANGED_VIA_REVERT); 90 chrome::AttemptUserExit(); 91 } 92 93 void LocaleChangeGuard::RevertLocaleChangeCallback(const ListValue* list) { 94 RevertLocaleChange(); 95 } 96 97 void LocaleChangeGuard::Observe(int type, 98 const content::NotificationSource& source, 99 const content::NotificationDetails& details) { 100 if (profile_ == NULL) { 101 NOTREACHED(); 102 return; 103 } 104 switch (type) { 105 case chrome::NOTIFICATION_SESSION_STARTED: { 106 session_started_ = true; 107 registrar_.Remove(this, chrome::NOTIFICATION_SESSION_STARTED, 108 content::NotificationService::AllSources()); 109 if (main_frame_loaded_) 110 Check(); 111 break; 112 } 113 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: { 114 if (profile_ == 115 content::Source<WebContents>(source)->GetBrowserContext()) { 116 main_frame_loaded_ = true; 117 // We need to perform locale change check only once, so unsubscribe. 118 registrar_.Remove(this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, 119 content::NotificationService::AllSources()); 120 if (session_started_) 121 Check(); 122 } 123 break; 124 } 125 case chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED: { 126 if (DeviceSettingsService::Get()->HasPrivateOwnerKey()) { 127 PrefService* local_state = g_browser_process->local_state(); 128 if (local_state) { 129 PrefService* prefs = profile_->GetPrefs(); 130 if (prefs == NULL) { 131 NOTREACHED(); 132 return; 133 } 134 std::string owner_locale = 135 prefs->GetString(prefs::kApplicationLocale); 136 if (!owner_locale.empty()) 137 local_state->SetString(prefs::kOwnerLocale, owner_locale); 138 } 139 } 140 break; 141 } 142 default: { 143 NOTREACHED(); 144 break; 145 } 146 } 147 } 148 149 void LocaleChangeGuard::Check() { 150 std::string cur_locale = g_browser_process->GetApplicationLocale(); 151 if (cur_locale.empty()) { 152 NOTREACHED(); 153 return; 154 } 155 156 PrefService* prefs = profile_->GetPrefs(); 157 if (prefs == NULL) { 158 NOTREACHED(); 159 return; 160 } 161 162 std::string to_locale = prefs->GetString(prefs::kApplicationLocale); 163 if (to_locale != cur_locale) { 164 // This conditional branch can occur in cases like: 165 // (1) kApplicationLocale preference was modified by synchronization; 166 // (2) kApplicationLocale is managed by policy. 167 return; 168 } 169 170 std::string from_locale = prefs->GetString(prefs::kApplicationLocaleBackup); 171 if (from_locale.empty() || from_locale == to_locale) 172 return; // No locale change was detected, just exit. 173 174 if (prefs->GetString(prefs::kApplicationLocaleAccepted) == to_locale) 175 return; // Already accepted. 176 177 // Locale change detected, showing notification. 178 if (from_locale_ != from_locale || to_locale_ != to_locale) { 179 // Falling back to showing message in current locale. 180 LOG(ERROR) << 181 "Showing locale change notification in current (not previous) language"; 182 PrepareChangingLocale(from_locale, to_locale); 183 } 184 185 ash::Shell::GetInstance()->system_tray_notifier()->NotifyLocaleChanged( 186 this, cur_locale, from_locale_, to_locale_); 187 } 188 189 void LocaleChangeGuard::AcceptLocaleChange() { 190 if (profile_ == NULL || 191 from_locale_.empty() || 192 to_locale_.empty()) { 193 NOTREACHED(); 194 return; 195 } 196 197 // Check whether locale has been reverted or changed. 198 // If not: mark current locale as accepted. 199 if (reverted_) 200 return; 201 PrefService* prefs = profile_->GetPrefs(); 202 if (prefs == NULL) { 203 NOTREACHED(); 204 return; 205 } 206 if (prefs->GetString(prefs::kApplicationLocale) != to_locale_) 207 return; 208 content::RecordAction(UserMetricsAction("LanguageChange_Accept")); 209 prefs->SetString(prefs::kApplicationLocaleBackup, to_locale_); 210 prefs->SetString(prefs::kApplicationLocaleAccepted, to_locale_); 211 } 212 213 void LocaleChangeGuard::PrepareChangingLocale( 214 const std::string& from_locale, const std::string& to_locale) { 215 std::string cur_locale = g_browser_process->GetApplicationLocale(); 216 if (!from_locale.empty()) 217 from_locale_ = from_locale; 218 if (!to_locale.empty()) 219 to_locale_ = to_locale; 220 221 if (!from_locale_.empty() && !to_locale_.empty()) { 222 base::string16 from = l10n_util::GetDisplayNameForLocale( 223 from_locale_, cur_locale, true); 224 base::string16 to = l10n_util::GetDisplayNameForLocale( 225 to_locale_, cur_locale, true); 226 227 title_text_ = l10n_util::GetStringUTF16( 228 IDS_OPTIONS_SETTINGS_SECTION_TITLE_LANGUAGE); 229 message_text_ = l10n_util::GetStringFUTF16( 230 IDS_LOCALE_CHANGE_MESSAGE, from, to); 231 revert_link_text_ = l10n_util::GetStringFUTF16( 232 IDS_LOCALE_CHANGE_REVERT_MESSAGE, from); 233 } 234 } 235 236 void LocaleChangeGuard::Delegate::Close(bool by_user) { 237 if (by_user) 238 master_->AcceptLocaleChange(); 239 } 240 241 std::string LocaleChangeGuard::Delegate::id() const { 242 // Arbitrary unique Id. 243 return "8c386938-1e3f-11e0-ac7b-18a90520e2e5"; 244 } 245 246 } // namespace chromeos 247