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
32using content::UserMetricsAction;
33using content::WebContents;
34
35namespace chromeos {
36
37class 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
58LocaleChangeGuard::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
68LocaleChangeGuard::~LocaleChangeGuard() {}
69
70void 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
77void 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
93void LocaleChangeGuard::RevertLocaleChangeCallback(const ListValue* list) {
94  RevertLocaleChange();
95}
96
97void 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
149void 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
189void 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
213void 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    string16 from = l10n_util::GetDisplayNameForLocale(
223        from_locale_, cur_locale, true);
224    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
236void LocaleChangeGuard::Delegate::Close(bool by_user) {
237  if (by_user)
238    master_->AcceptLocaleChange();
239}
240
241std::string LocaleChangeGuard::Delegate::id() const {
242  // Arbitrary unique Id.
243  return "8c386938-1e3f-11e0-ac7b-18a90520e2e5";
244}
245
246}  // namespace chromeos
247