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
36 namespace extensions {
37
38 namespace fonts = api::font_settings;
39
40 namespace {
41
42 const char kFontIdKey[] = "fontId";
43 const char kGenericFamilyKey[] = "genericFamily";
44 const char kLevelOfControlKey[] = "levelOfControl";
45 const char kDisplayNameKey[] = "displayName";
46 const char kPixelSizeKey[] = "pixelSize";
47 const char kScriptKey[] = "script";
48
49 const char kSetFromIncognitoError[] =
50 "Can't modify regular settings from an incognito context.";
51
52 // Format for font name preference paths.
53 const 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.
GetFontNamePrefPath(fonts::GenericFamily generic_family_enum,fonts::ScriptCode script_enum)57 std::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.
MaybeGetLocalizedFontName(const std::string & font_name)71 std::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|.
RegisterFontFamilyMapObserver(PrefChangeRegistrar * registrar,const char * map_name,const PrefChangeRegistrar::NamedChangeCallback & callback)83 void 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
FontSettingsEventRouter(Profile * profile)96 FontSettingsEventRouter::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(®istrar_,
114 prefs::kWebKitStandardFontFamilyMap, callback);
115 RegisterFontFamilyMapObserver(®istrar_,
116 prefs::kWebKitSerifFontFamilyMap, callback);
117 RegisterFontFamilyMapObserver(®istrar_,
118 prefs::kWebKitSansSerifFontFamilyMap, callback);
119 RegisterFontFamilyMapObserver(®istrar_,
120 prefs::kWebKitFixedFontFamilyMap, callback);
121 RegisterFontFamilyMapObserver(®istrar_,
122 prefs::kWebKitCursiveFontFamilyMap, callback);
123 RegisterFontFamilyMapObserver(®istrar_,
124 prefs::kWebKitFantasyFontFamilyMap, callback);
125 RegisterFontFamilyMapObserver(®istrar_,
126 prefs::kWebKitPictographFontFamilyMap,
127 callback);
128 }
129
~FontSettingsEventRouter()130 FontSettingsEventRouter::~FontSettingsEventRouter() {}
131
AddPrefToObserve(const char * pref_name,const char * event_name,const char * key)132 void 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
OnFontFamilyMapPrefChanged(const std::string & pref_name)141 void 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
OnFontNamePrefChanged(const std::string & pref_name,const std::string & generic_family,const std::string & script)154 void 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
OnFontPrefChanged(const std::string & event_name,const std::string & key,const std::string & pref_name)185 void 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
FontSettingsAPI(content::BrowserContext * context)207 FontSettingsAPI::FontSettingsAPI(content::BrowserContext* context)
208 : font_settings_event_router_(
209 new FontSettingsEventRouter(Profile::FromBrowserContext(context))) {}
210
~FontSettingsAPI()211 FontSettingsAPI::~FontSettingsAPI() {
212 }
213
214 static base::LazyInstance<BrowserContextKeyedAPIFactory<FontSettingsAPI> >
215 g_factory = LAZY_INSTANCE_INITIALIZER;
216
217 // static
218 BrowserContextKeyedAPIFactory<FontSettingsAPI>*
GetFactoryInstance()219 FontSettingsAPI::GetFactoryInstance() {
220 return g_factory.Pointer();
221 }
222
RunSync()223 bool 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
RunSync()245 bool 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
RunSync()276 bool 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
RunAsync()301 bool FontSettingsGetFontListFunction::RunAsync() {
302 content::GetFontListAsync(
303 Bind(&FontSettingsGetFontListFunction::FontListHasLoaded, this));
304 return true;
305 }
306
FontListHasLoaded(scoped_ptr<base::ListValue> list)307 void FontSettingsGetFontListFunction::FontListHasLoaded(
308 scoped_ptr<base::ListValue> list) {
309 bool success = CopyFontsToResult(list.get());
310 SendResponse(success);
311 }
312
CopyFontsToResult(base::ListValue * fonts)313 bool 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
RunSync()346 bool 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
RunSync()357 bool 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
RunSync()377 bool 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
GetPrefName()397 const char* FontSettingsClearDefaultFontSizeFunction::GetPrefName() {
398 return prefs::kWebKitDefaultFontSize;
399 }
400
GetPrefName()401 const char* FontSettingsGetDefaultFontSizeFunction::GetPrefName() {
402 return prefs::kWebKitDefaultFontSize;
403 }
404
GetKey()405 const char* FontSettingsGetDefaultFontSizeFunction::GetKey() {
406 return kPixelSizeKey;
407 }
408
GetPrefName()409 const char* FontSettingsSetDefaultFontSizeFunction::GetPrefName() {
410 return prefs::kWebKitDefaultFontSize;
411 }
412
GetKey()413 const char* FontSettingsSetDefaultFontSizeFunction::GetKey() {
414 return kPixelSizeKey;
415 }
416
GetPrefName()417 const char* FontSettingsClearDefaultFixedFontSizeFunction::GetPrefName() {
418 return prefs::kWebKitDefaultFixedFontSize;
419 }
420
GetPrefName()421 const char* FontSettingsGetDefaultFixedFontSizeFunction::GetPrefName() {
422 return prefs::kWebKitDefaultFixedFontSize;
423 }
424
GetKey()425 const char* FontSettingsGetDefaultFixedFontSizeFunction::GetKey() {
426 return kPixelSizeKey;
427 }
428
GetPrefName()429 const char* FontSettingsSetDefaultFixedFontSizeFunction::GetPrefName() {
430 return prefs::kWebKitDefaultFixedFontSize;
431 }
432
GetKey()433 const char* FontSettingsSetDefaultFixedFontSizeFunction::GetKey() {
434 return kPixelSizeKey;
435 }
436
GetPrefName()437 const char* FontSettingsClearMinimumFontSizeFunction::GetPrefName() {
438 return prefs::kWebKitMinimumFontSize;
439 }
440
GetPrefName()441 const char* FontSettingsGetMinimumFontSizeFunction::GetPrefName() {
442 return prefs::kWebKitMinimumFontSize;
443 }
444
GetKey()445 const char* FontSettingsGetMinimumFontSizeFunction::GetKey() {
446 return kPixelSizeKey;
447 }
448
GetPrefName()449 const char* FontSettingsSetMinimumFontSizeFunction::GetPrefName() {
450 return prefs::kWebKitMinimumFontSize;
451 }
452
GetKey()453 const char* FontSettingsSetMinimumFontSizeFunction::GetKey() {
454 return kPixelSizeKey;
455 }
456
457 } // namespace extensions
458