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// Font Settings Extension API implementation.
6
7#include "chrome/browser/extensions/api/font_settings/font_settings_api.h"
8
9#include "base/bind.h"
10#include "base/command_line.h"
11#include "base/json/json_writer.h"
12#include "base/lazy_instance.h"
13#include "base/prefs/pref_service.h"
14#include "base/strings/string_util.h"
15#include "base/strings/stringprintf.h"
16#include "base/values.h"
17#include "chrome/browser/chrome_notification_types.h"
18#include "chrome/browser/extensions/api/preference/preference_api.h"
19#include "chrome/browser/extensions/api/preference/preference_helpers.h"
20#include "chrome/browser/extensions/extension_service.h"
21#include "chrome/browser/profiles/profile.h"
22#include "chrome/common/extensions/api/font_settings.h"
23#include "chrome/common/pref_names.h"
24#include "chrome/common/pref_names_util.h"
25#include "content/public/browser/font_list_async.h"
26#include "content/public/browser/notification_details.h"
27#include "content/public/browser/notification_source.h"
28#include "extensions/browser/extension_system.h"
29#include "extensions/common/error_utils.h"
30
31#if defined(OS_WIN)
32#include "ui/gfx/font.h"
33#include "ui/gfx/platform_font_win.h"
34#endif
35
36namespace extensions {
37
38namespace fonts = api::font_settings;
39
40namespace {
41
42const char kFontIdKey[] = "fontId";
43const char kGenericFamilyKey[] = "genericFamily";
44const char kLevelOfControlKey[] = "levelOfControl";
45const char kDisplayNameKey[] = "displayName";
46const char kPixelSizeKey[] = "pixelSize";
47const char kScriptKey[] = "script";
48
49const char kSetFromIncognitoError[] =
50    "Can't modify regular settings from an incognito context.";
51
52// Format for font name preference paths.
53const char kWebKitFontPrefFormat[] = "webkit.webprefs.fonts.%s.%s";
54
55// Gets the font name preference path for |generic_family| and |script|. If
56// |script| is NULL, uses prefs::kWebKitCommonScript.
57std::string GetFontNamePrefPath(fonts::GenericFamily generic_family_enum,
58                                fonts::ScriptCode script_enum) {
59  std::string script = fonts::ToString(script_enum);
60  if (script.empty())
61    script = prefs::kWebKitCommonScript;
62  std::string generic_family = fonts::ToString(generic_family_enum);
63  return base::StringPrintf(kWebKitFontPrefFormat,
64                            generic_family.c_str(),
65                            script.c_str());
66}
67
68// Returns the localized name of a font so that it can be matched within the
69// list of system fonts. On Windows, the list of system fonts has names only
70// for the system locale, but the pref value may be in the English name.
71std::string MaybeGetLocalizedFontName(const std::string& font_name) {
72#if defined(OS_WIN)
73  if (!font_name.empty()) {
74    gfx::Font font(font_name, 12);  // dummy font size
75    return static_cast<gfx::PlatformFontWin*>(font.platform_font())->
76        GetLocalizedFontName();
77  }
78#endif
79  return font_name;
80}
81
82// Registers |obs| to observe per-script font prefs under the path |map_name|.
83void RegisterFontFamilyMapObserver(
84    PrefChangeRegistrar* registrar,
85    const char* map_name,
86    const PrefChangeRegistrar::NamedChangeCallback& callback) {
87  for (size_t i = 0; i < prefs::kWebKitScriptsForFontFamilyMapsLength; ++i) {
88    const char* script = prefs::kWebKitScriptsForFontFamilyMaps[i];
89    std::string pref_name = base::StringPrintf("%s.%s", map_name, script);
90    registrar->Add(pref_name.c_str(), callback);
91  }
92}
93
94}  // namespace
95
96FontSettingsEventRouter::FontSettingsEventRouter(
97    Profile* profile) : profile_(profile) {
98  registrar_.Init(profile_->GetPrefs());
99
100  AddPrefToObserve(prefs::kWebKitDefaultFixedFontSize,
101                   fonts::OnDefaultFixedFontSizeChanged::kEventName,
102                   kPixelSizeKey);
103  AddPrefToObserve(prefs::kWebKitDefaultFontSize,
104                   fonts::OnDefaultFontSizeChanged::kEventName,
105                   kPixelSizeKey);
106  AddPrefToObserve(prefs::kWebKitMinimumFontSize,
107                   fonts::OnMinimumFontSizeChanged::kEventName,
108                   kPixelSizeKey);
109
110  PrefChangeRegistrar::NamedChangeCallback callback =
111      base::Bind(&FontSettingsEventRouter::OnFontFamilyMapPrefChanged,
112                 base::Unretained(this));
113  RegisterFontFamilyMapObserver(&registrar_,
114                                prefs::kWebKitStandardFontFamilyMap, callback);
115  RegisterFontFamilyMapObserver(&registrar_,
116                                prefs::kWebKitSerifFontFamilyMap, callback);
117  RegisterFontFamilyMapObserver(&registrar_,
118                                prefs::kWebKitSansSerifFontFamilyMap, callback);
119  RegisterFontFamilyMapObserver(&registrar_,
120                                prefs::kWebKitFixedFontFamilyMap, callback);
121  RegisterFontFamilyMapObserver(&registrar_,
122                                prefs::kWebKitCursiveFontFamilyMap, callback);
123  RegisterFontFamilyMapObserver(&registrar_,
124                                prefs::kWebKitFantasyFontFamilyMap, callback);
125  RegisterFontFamilyMapObserver(&registrar_,
126                                prefs::kWebKitPictographFontFamilyMap,
127                                callback);
128}
129
130FontSettingsEventRouter::~FontSettingsEventRouter() {}
131
132void FontSettingsEventRouter::AddPrefToObserve(const char* pref_name,
133                                               const char* event_name,
134                                               const char* key) {
135  registrar_.Add(pref_name,
136                 base::Bind(&FontSettingsEventRouter::OnFontPrefChanged,
137                            base::Unretained(this),
138                            event_name, key));
139}
140
141void FontSettingsEventRouter::OnFontFamilyMapPrefChanged(
142    const std::string& pref_name) {
143  std::string generic_family;
144  std::string script;
145  if (pref_names_util::ParseFontNamePrefPath(pref_name, &generic_family,
146                                             &script)) {
147    OnFontNamePrefChanged(pref_name, generic_family, script);
148    return;
149  }
150
151  NOTREACHED();
152}
153
154void FontSettingsEventRouter::OnFontNamePrefChanged(
155    const std::string& pref_name,
156    const std::string& generic_family,
157    const std::string& script) {
158  const PrefService::Preference* pref = registrar_.prefs()->FindPreference(
159      pref_name.c_str());
160  CHECK(pref);
161
162  std::string font_name;
163  if (!pref->GetValue()->GetAsString(&font_name)) {
164    NOTREACHED();
165    return;
166  }
167  font_name = MaybeGetLocalizedFontName(font_name);
168
169  base::ListValue args;
170  base::DictionaryValue* dict = new base::DictionaryValue();
171  args.Append(dict);
172  dict->SetString(kFontIdKey, font_name);
173  dict->SetString(kGenericFamilyKey, generic_family);
174  dict->SetString(kScriptKey, script);
175
176  extensions::preference_helpers::DispatchEventToExtensions(
177      profile_,
178      fonts::OnFontChanged::kEventName,
179      &args,
180      APIPermission::kFontSettings,
181      false,
182      pref_name);
183}
184
185void FontSettingsEventRouter::OnFontPrefChanged(
186    const std::string& event_name,
187    const std::string& key,
188    const std::string& pref_name) {
189  const PrefService::Preference* pref = registrar_.prefs()->FindPreference(
190      pref_name.c_str());
191  CHECK(pref);
192
193  base::ListValue args;
194  base::DictionaryValue* dict = new base::DictionaryValue();
195  args.Append(dict);
196  dict->Set(key, pref->GetValue()->DeepCopy());
197
198  extensions::preference_helpers::DispatchEventToExtensions(
199      profile_,
200      event_name,
201      &args,
202      APIPermission::kFontSettings,
203      false,
204      pref_name);
205}
206
207FontSettingsAPI::FontSettingsAPI(content::BrowserContext* context)
208    : font_settings_event_router_(
209          new FontSettingsEventRouter(Profile::FromBrowserContext(context))) {}
210
211FontSettingsAPI::~FontSettingsAPI() {
212}
213
214static base::LazyInstance<BrowserContextKeyedAPIFactory<FontSettingsAPI> >
215    g_factory = LAZY_INSTANCE_INITIALIZER;
216
217// static
218BrowserContextKeyedAPIFactory<FontSettingsAPI>*
219FontSettingsAPI::GetFactoryInstance() {
220  return g_factory.Pointer();
221}
222
223bool FontSettingsClearFontFunction::RunSync() {
224  if (GetProfile()->IsOffTheRecord()) {
225    error_ = kSetFromIncognitoError;
226    return false;
227  }
228
229  scoped_ptr<fonts::ClearFont::Params> params(
230      fonts::ClearFont::Params::Create(*args_));
231  EXTENSION_FUNCTION_VALIDATE(params.get());
232
233  std::string pref_path = GetFontNamePrefPath(params->details.generic_family,
234                                              params->details.script);
235
236  // Ensure |pref_path| really is for a registered per-script font pref.
237  EXTENSION_FUNCTION_VALIDATE(
238      GetProfile()->GetPrefs()->FindPreference(pref_path.c_str()));
239
240  PreferenceAPI::Get(GetProfile())->RemoveExtensionControlledPref(
241      extension_id(), pref_path.c_str(), kExtensionPrefsScopeRegular);
242  return true;
243}
244
245bool FontSettingsGetFontFunction::RunSync() {
246  scoped_ptr<fonts::GetFont::Params> params(
247      fonts::GetFont::Params::Create(*args_));
248  EXTENSION_FUNCTION_VALIDATE(params.get());
249
250  std::string pref_path = GetFontNamePrefPath(params->details.generic_family,
251                                              params->details.script);
252
253  PrefService* prefs = GetProfile()->GetPrefs();
254  const PrefService::Preference* pref =
255      prefs->FindPreference(pref_path.c_str());
256
257  std::string font_name;
258  EXTENSION_FUNCTION_VALIDATE(
259      pref && pref->GetValue()->GetAsString(&font_name));
260  font_name = MaybeGetLocalizedFontName(font_name);
261
262  // We don't support incognito-specific font prefs, so don't consider them when
263  // getting level of control.
264  const bool kIncognito = false;
265  std::string level_of_control =
266      extensions::preference_helpers::GetLevelOfControl(
267          GetProfile(), extension_id(), pref_path, kIncognito);
268
269  base::DictionaryValue* result = new base::DictionaryValue();
270  result->SetString(kFontIdKey, font_name);
271  result->SetString(kLevelOfControlKey, level_of_control);
272  SetResult(result);
273  return true;
274}
275
276bool FontSettingsSetFontFunction::RunSync() {
277  if (GetProfile()->IsOffTheRecord()) {
278    error_ = kSetFromIncognitoError;
279    return false;
280  }
281
282  scoped_ptr<fonts::SetFont::Params> params(
283      fonts::SetFont::Params::Create(*args_));
284  EXTENSION_FUNCTION_VALIDATE(params.get());
285
286  std::string pref_path = GetFontNamePrefPath(params->details.generic_family,
287                                              params->details.script);
288
289  // Ensure |pref_path| really is for a registered font pref.
290  EXTENSION_FUNCTION_VALIDATE(
291      GetProfile()->GetPrefs()->FindPreference(pref_path.c_str()));
292
293  PreferenceAPI::Get(GetProfile())->SetExtensionControlledPref(
294      extension_id(),
295      pref_path.c_str(),
296      kExtensionPrefsScopeRegular,
297      new base::StringValue(params->details.font_id));
298  return true;
299}
300
301bool FontSettingsGetFontListFunction::RunAsync() {
302  content::GetFontListAsync(
303      Bind(&FontSettingsGetFontListFunction::FontListHasLoaded, this));
304  return true;
305}
306
307void FontSettingsGetFontListFunction::FontListHasLoaded(
308    scoped_ptr<base::ListValue> list) {
309  bool success = CopyFontsToResult(list.get());
310  SendResponse(success);
311}
312
313bool FontSettingsGetFontListFunction::CopyFontsToResult(
314    base::ListValue* fonts) {
315  scoped_ptr<base::ListValue> result(new base::ListValue());
316  for (base::ListValue::iterator it = fonts->begin();
317       it != fonts->end(); ++it) {
318    base::ListValue* font_list_value;
319    if (!(*it)->GetAsList(&font_list_value)) {
320      NOTREACHED();
321      return false;
322    }
323
324    std::string name;
325    if (!font_list_value->GetString(0, &name)) {
326      NOTREACHED();
327      return false;
328    }
329
330    std::string localized_name;
331    if (!font_list_value->GetString(1, &localized_name)) {
332      NOTREACHED();
333      return false;
334    }
335
336    base::DictionaryValue* font_name = new base::DictionaryValue();
337    font_name->Set(kFontIdKey, new base::StringValue(name));
338    font_name->Set(kDisplayNameKey, new base::StringValue(localized_name));
339    result->Append(font_name);
340  }
341
342  SetResult(result.release());
343  return true;
344}
345
346bool ClearFontPrefExtensionFunction::RunSync() {
347  if (GetProfile()->IsOffTheRecord()) {
348    error_ = kSetFromIncognitoError;
349    return false;
350  }
351
352  PreferenceAPI::Get(GetProfile())->RemoveExtensionControlledPref(
353      extension_id(), GetPrefName(), kExtensionPrefsScopeRegular);
354  return true;
355}
356
357bool GetFontPrefExtensionFunction::RunSync() {
358  PrefService* prefs = GetProfile()->GetPrefs();
359  const PrefService::Preference* pref = prefs->FindPreference(GetPrefName());
360  EXTENSION_FUNCTION_VALIDATE(pref);
361
362  // We don't support incognito-specific font prefs, so don't consider them when
363  // getting level of control.
364  const bool kIncognito = false;
365
366  std::string level_of_control =
367      extensions::preference_helpers::GetLevelOfControl(
368          GetProfile(), extension_id(), GetPrefName(), kIncognito);
369
370  base::DictionaryValue* result = new base::DictionaryValue();
371  result->Set(GetKey(), pref->GetValue()->DeepCopy());
372  result->SetString(kLevelOfControlKey, level_of_control);
373  SetResult(result);
374  return true;
375}
376
377bool SetFontPrefExtensionFunction::RunSync() {
378  if (GetProfile()->IsOffTheRecord()) {
379    error_ = kSetFromIncognitoError;
380    return false;
381  }
382
383  base::DictionaryValue* details = NULL;
384  EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &details));
385
386  base::Value* value;
387  EXTENSION_FUNCTION_VALIDATE(details->Get(GetKey(), &value));
388
389  PreferenceAPI::Get(GetProfile())
390      ->SetExtensionControlledPref(extension_id(),
391                                   GetPrefName(),
392                                   kExtensionPrefsScopeRegular,
393                                   value->DeepCopy());
394  return true;
395}
396
397const char* FontSettingsClearDefaultFontSizeFunction::GetPrefName() {
398  return prefs::kWebKitDefaultFontSize;
399}
400
401const char* FontSettingsGetDefaultFontSizeFunction::GetPrefName() {
402  return prefs::kWebKitDefaultFontSize;
403}
404
405const char* FontSettingsGetDefaultFontSizeFunction::GetKey() {
406  return kPixelSizeKey;
407}
408
409const char* FontSettingsSetDefaultFontSizeFunction::GetPrefName() {
410  return prefs::kWebKitDefaultFontSize;
411}
412
413const char* FontSettingsSetDefaultFontSizeFunction::GetKey() {
414  return kPixelSizeKey;
415}
416
417const char* FontSettingsClearDefaultFixedFontSizeFunction::GetPrefName() {
418  return prefs::kWebKitDefaultFixedFontSize;
419}
420
421const char* FontSettingsGetDefaultFixedFontSizeFunction::GetPrefName() {
422  return prefs::kWebKitDefaultFixedFontSize;
423}
424
425const char* FontSettingsGetDefaultFixedFontSizeFunction::GetKey() {
426  return kPixelSizeKey;
427}
428
429const char* FontSettingsSetDefaultFixedFontSizeFunction::GetPrefName() {
430  return prefs::kWebKitDefaultFixedFontSize;
431}
432
433const char* FontSettingsSetDefaultFixedFontSizeFunction::GetKey() {
434  return kPixelSizeKey;
435}
436
437const char* FontSettingsClearMinimumFontSizeFunction::GetPrefName() {
438  return prefs::kWebKitMinimumFontSize;
439}
440
441const char* FontSettingsGetMinimumFontSizeFunction::GetPrefName() {
442  return prefs::kWebKitMinimumFontSize;
443}
444
445const char* FontSettingsGetMinimumFontSizeFunction::GetKey() {
446  return kPixelSizeKey;
447}
448
449const char* FontSettingsSetMinimumFontSizeFunction::GetPrefName() {
450  return prefs::kWebKitMinimumFontSize;
451}
452
453const char* FontSettingsSetMinimumFontSizeFunction::GetKey() {
454  return kPixelSizeKey;
455}
456
457}  // namespace extensions
458