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/ui/webui/options/font_settings_handler.h"
6
7#include <string>
8
9#include "base/basictypes.h"
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/i18n/rtl.h"
13#include "base/prefs/pref_service.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/strings/string_util.h"
16#include "base/strings/utf_string_conversions.h"
17#include "base/values.h"
18#include "chrome/browser/browser_process.h"
19#include "chrome/browser/character_encoding.h"
20#include "chrome/browser/extensions/extension_service.h"
21#include "chrome/browser/extensions/extension_tab_util.h"
22#include "chrome/browser/profiles/profile.h"
23#include "chrome/browser/ui/browser_finder.h"
24#include "chrome/browser/ui/webui/options/font_settings_utils.h"
25#include "chrome/common/pref_names.h"
26#include "chrome/grit/generated_resources.h"
27#include "content/public/browser/font_list_async.h"
28#include "content/public/browser/notification_details.h"
29#include "content/public/browser/notification_service.h"
30#include "content/public/browser/web_ui.h"
31#include "extensions/browser/extension_registry.h"
32#include "extensions/browser/extension_system.h"
33#include "extensions/common/extension.h"
34#include "extensions/common/extension_urls.h"
35#include "ui/base/l10n/l10n_util.h"
36#include "url/gurl.h"
37
38#if defined(OS_WIN)
39#include "ui/gfx/font.h"
40#include "ui/gfx/platform_font_win.h"
41#endif
42
43namespace {
44
45// Returns the localized name of a font so that settings can find it within the
46// list of system fonts. On Windows, the list of system fonts has names only
47// for the system locale, but the pref value may be in the English name.
48std::string MaybeGetLocalizedFontName(const std::string& font_name) {
49#if defined(OS_WIN)
50  gfx::Font font(font_name, 12);  // dummy font size
51  return static_cast<gfx::PlatformFontWin*>(font.platform_font())->
52      GetLocalizedFontName();
53#else
54  return font_name;
55#endif
56}
57
58const char kAdvancedFontSettingsExtensionId[] =
59    "caclkomlalccbpcdllchkeecicepbmbm";
60
61}  // namespace
62
63
64namespace options {
65
66FontSettingsHandler::FontSettingsHandler()
67    : extension_registry_observer_(this) {
68}
69
70FontSettingsHandler::~FontSettingsHandler() {
71}
72
73void FontSettingsHandler::GetLocalizedValues(
74    base::DictionaryValue* localized_strings) {
75  DCHECK(localized_strings);
76
77  static OptionsStringResource resources[] = {
78    { "fontSettingsStandard",
79      IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_STANDARD_LABEL },
80    { "fontSettingsSerif",
81      IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_SERIF_LABEL },
82    { "fontSettingsSansSerif",
83      IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_SANS_SERIF_LABEL },
84    { "fontSettingsFixedWidth",
85      IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_FIXED_WIDTH_LABEL },
86    { "fontSettingsMinimumSize",
87      IDS_FONT_LANGUAGE_SETTING_MINIMUM_FONT_SIZE_TITLE },
88    { "fontSettingsEncoding",
89      IDS_FONT_LANGUAGE_SETTING_FONT_SUB_DIALOG_ENCODING_TITLE },
90    { "fontSettingsSizeTiny",
91      IDS_FONT_LANGUAGE_SETTING_FONT_SIZE_TINY },
92    { "fontSettingsSizeHuge",
93      IDS_FONT_LANGUAGE_SETTING_FONT_SIZE_HUGE },
94    { "fontSettingsLoremIpsum",
95      IDS_FONT_LANGUAGE_SETTING_LOREM_IPSUM },
96    { "advancedFontSettingsOptions",
97      IDS_FONT_LANGUAGE_SETTING_ADVANCED_FONT_SETTINGS_OPTIONS }
98  };
99
100  RegisterStrings(localized_strings, resources, arraysize(resources));
101  RegisterTitle(localized_strings, "fontSettingsPage",
102                IDS_FONT_LANGUAGE_SETTING_FONT_TAB_TITLE);
103  localized_strings->SetString("fontSettingsPlaceholder",
104      l10n_util::GetStringUTF16(
105          IDS_FONT_LANGUAGE_SETTING_PLACEHOLDER));
106
107  GURL install_url(extension_urls::GetWebstoreItemDetailURLPrefix());
108  localized_strings->SetString("advancedFontSettingsInstall",
109      l10n_util::GetStringFUTF16(
110          IDS_FONT_LANGUAGE_SETTING_ADVANCED_FONT_SETTINGS_INSTALL,
111          base::UTF8ToUTF16(
112              install_url.Resolve(kAdvancedFontSettingsExtensionId).spec())));
113}
114
115void FontSettingsHandler::InitializeHandler() {
116  Profile* profile = Profile::FromWebUI(web_ui());
117  extension_registry_observer_.Add(extensions::ExtensionRegistry::Get(profile));
118}
119
120void FontSettingsHandler::InitializePage() {
121  DCHECK(web_ui());
122  SetUpStandardFontSample();
123  SetUpSerifFontSample();
124  SetUpSansSerifFontSample();
125  SetUpFixedFontSample();
126  SetUpMinimumFontSample();
127  NotifyAdvancedFontSettingsAvailability();
128}
129
130void FontSettingsHandler::RegisterMessages() {
131  // Perform validation for saved fonts.
132  PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
133  FontSettingsUtilities::ValidateSavedFonts(pref_service);
134
135  // Register for preferences that we need to observe manually.
136  font_encoding_.Init(prefs::kDefaultCharset, pref_service);
137
138  standard_font_.Init(prefs::kWebKitStandardFontFamily,
139                      pref_service,
140                      base::Bind(&FontSettingsHandler::SetUpStandardFontSample,
141                                 base::Unretained(this)));
142  serif_font_.Init(prefs::kWebKitSerifFontFamily,
143                   pref_service,
144                   base::Bind(&FontSettingsHandler::SetUpSerifFontSample,
145                              base::Unretained(this)));
146  sans_serif_font_.Init(
147      prefs::kWebKitSansSerifFontFamily,
148      pref_service,
149      base::Bind(&FontSettingsHandler::SetUpSansSerifFontSample,
150                 base::Unretained(this)));
151
152  base::Closure callback = base::Bind(
153      &FontSettingsHandler::SetUpFixedFontSample, base::Unretained(this));
154
155  fixed_font_.Init(prefs::kWebKitFixedFontFamily, pref_service, callback);
156  default_fixed_font_size_.Init(prefs::kWebKitDefaultFixedFontSize,
157                                pref_service, callback);
158  default_font_size_.Init(
159      prefs::kWebKitDefaultFontSize,
160      pref_service,
161      base::Bind(&FontSettingsHandler::OnWebKitDefaultFontSizeChanged,
162                 base::Unretained(this)));
163  minimum_font_size_.Init(
164      prefs::kWebKitMinimumFontSize,
165      pref_service,
166      base::Bind(&FontSettingsHandler::SetUpMinimumFontSample,
167                 base::Unretained(this)));
168
169  web_ui()->RegisterMessageCallback("fetchFontsData",
170      base::Bind(&FontSettingsHandler::HandleFetchFontsData,
171                 base::Unretained(this)));
172  web_ui()->RegisterMessageCallback("openAdvancedFontSettingsOptions",
173      base::Bind(&FontSettingsHandler::HandleOpenAdvancedFontSettingsOptions,
174                 base::Unretained(this)));
175}
176
177void FontSettingsHandler::OnExtensionLoaded(
178    content::BrowserContext* browser_context,
179    const extensions::Extension* extension) {
180  NotifyAdvancedFontSettingsAvailability();
181}
182
183void FontSettingsHandler::OnExtensionUnloaded(
184    content::BrowserContext* browser_context,
185    const extensions::Extension* extension,
186    extensions::UnloadedExtensionInfo::Reason reason) {
187  NotifyAdvancedFontSettingsAvailability();
188}
189
190void FontSettingsHandler::HandleFetchFontsData(const base::ListValue* args) {
191  content::GetFontListAsync(
192      base::Bind(&FontSettingsHandler::FontsListHasLoaded,
193                 base::Unretained(this)));
194}
195
196void FontSettingsHandler::FontsListHasLoaded(
197    scoped_ptr<base::ListValue> list) {
198  // Selects the directionality for the fonts in the given list.
199  for (size_t i = 0; i < list->GetSize(); i++) {
200    base::ListValue* font;
201    bool has_font = list->GetList(i, &font);
202    DCHECK(has_font);
203    base::string16 value;
204    bool has_value = font->GetString(1, &value);
205    DCHECK(has_value);
206    bool has_rtl_chars = base::i18n::StringContainsStrongRTLChars(value);
207    font->Append(new base::StringValue(has_rtl_chars ? "rtl" : "ltr"));
208  }
209
210  base::ListValue encoding_list;
211  const std::vector<CharacterEncoding::EncodingInfo>* encodings;
212  PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
213  encodings = CharacterEncoding::GetCurrentDisplayEncodings(
214      g_browser_process->GetApplicationLocale(),
215      pref_service->GetString(prefs::kStaticEncodings),
216      pref_service->GetString(prefs::kRecentlySelectedEncoding));
217  DCHECK(encodings);
218  DCHECK(!encodings->empty());
219
220  std::vector<CharacterEncoding::EncodingInfo>::const_iterator it;
221  for (it = encodings->begin(); it != encodings->end(); ++it) {
222    base::ListValue* option = new base::ListValue();
223    if (it->encoding_id) {
224      int cmd_id = it->encoding_id;
225      std::string encoding =
226      CharacterEncoding::GetCanonicalEncodingNameByCommandId(cmd_id);
227      base::string16 name = it->encoding_display_name;
228      bool has_rtl_chars = base::i18n::StringContainsStrongRTLChars(name);
229      option->Append(new base::StringValue(encoding));
230      option->Append(new base::StringValue(name));
231      option->Append(new base::StringValue(has_rtl_chars ? "rtl" : "ltr"));
232    } else {
233      // Add empty name/value to indicate a separator item.
234      option->Append(new base::StringValue(std::string()));
235      option->Append(new base::StringValue(std::string()));
236    }
237    encoding_list.Append(option);
238  }
239
240  base::ListValue selected_values;
241  selected_values.Append(new base::StringValue(MaybeGetLocalizedFontName(
242      standard_font_.GetValue())));
243  selected_values.Append(new base::StringValue(MaybeGetLocalizedFontName(
244      serif_font_.GetValue())));
245  selected_values.Append(new base::StringValue(MaybeGetLocalizedFontName(
246      sans_serif_font_.GetValue())));
247  selected_values.Append(new base::StringValue(MaybeGetLocalizedFontName(
248      fixed_font_.GetValue())));
249  selected_values.Append(new base::StringValue(font_encoding_.GetValue()));
250
251  web_ui()->CallJavascriptFunction("FontSettings.setFontsData",
252                                   *list.get(), encoding_list,
253                                   selected_values);
254}
255
256void FontSettingsHandler::SetUpStandardFontSample() {
257  base::StringValue font_value(standard_font_.GetValue());
258  base::FundamentalValue size_value(default_font_size_.GetValue());
259  web_ui()->CallJavascriptFunction(
260      "FontSettings.setUpStandardFontSample", font_value, size_value);
261}
262
263void FontSettingsHandler::SetUpSerifFontSample() {
264  base::StringValue font_value(serif_font_.GetValue());
265  base::FundamentalValue size_value(default_font_size_.GetValue());
266  web_ui()->CallJavascriptFunction(
267      "FontSettings.setUpSerifFontSample", font_value, size_value);
268}
269
270void FontSettingsHandler::SetUpSansSerifFontSample() {
271  base::StringValue font_value(sans_serif_font_.GetValue());
272  base::FundamentalValue size_value(default_font_size_.GetValue());
273  web_ui()->CallJavascriptFunction(
274      "FontSettings.setUpSansSerifFontSample", font_value, size_value);
275}
276
277void FontSettingsHandler::SetUpFixedFontSample() {
278  base::StringValue font_value(fixed_font_.GetValue());
279  base::FundamentalValue size_value(default_fixed_font_size_.GetValue());
280  web_ui()->CallJavascriptFunction(
281      "FontSettings.setUpFixedFontSample", font_value, size_value);
282}
283
284void FontSettingsHandler::SetUpMinimumFontSample() {
285  base::FundamentalValue size_value(minimum_font_size_.GetValue());
286  web_ui()->CallJavascriptFunction("FontSettings.setUpMinimumFontSample",
287                                   size_value);
288}
289
290const extensions::Extension*
291FontSettingsHandler::GetAdvancedFontSettingsExtension() {
292  Profile* profile = Profile::FromWebUI(web_ui());
293  ExtensionService* service =
294      extensions::ExtensionSystem::Get(profile)->extension_service();
295  if (!service->IsExtensionEnabled(kAdvancedFontSettingsExtensionId))
296    return NULL;
297  return service->GetInstalledExtension(kAdvancedFontSettingsExtensionId);
298}
299
300void FontSettingsHandler::NotifyAdvancedFontSettingsAvailability() {
301  web_ui()->CallJavascriptFunction(
302      "FontSettings.notifyAdvancedFontSettingsAvailability",
303      base::FundamentalValue(GetAdvancedFontSettingsExtension() != NULL));
304}
305
306void FontSettingsHandler::HandleOpenAdvancedFontSettingsOptions(
307    const base::ListValue* args) {
308  const extensions::Extension* extension = GetAdvancedFontSettingsExtension();
309  if (!extension)
310    return;
311  extensions::ExtensionTabUtil::OpenOptionsPage(extension,
312      chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()));
313}
314
315void FontSettingsHandler::OnWebKitDefaultFontSizeChanged() {
316  SetUpStandardFontSample();
317  SetUpSerifFontSample();
318  SetUpSansSerifFontSample();
319}
320
321}  // namespace options
322