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