blob: be6d53e8bd130dba5c91de741db5fa9143f7f168 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Font Settings Extension API implementation.
#include "chrome/browser/extensions/api/font_settings/font_settings_api.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/json/json_writer.h"
#include "base/lazy_instance.h"
#include "base/strings/string_util.h"
#include "base/trace_event/trace_event.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/api/preference/preference_helpers.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/font_pref_change_notifier_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/font_settings.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_names_util.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/font_list_async.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "extensions/browser/extension_prefs_helper.h"
#include "extensions/browser/extension_prefs_helper_factory.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/error_utils.h"
#if BUILDFLAG(IS_WIN)
#include "ui/gfx/win/direct_write.h"
#endif // BUILDFLAG(IS_WIN)
namespace extensions {
namespace fonts = api::font_settings;
namespace {
const char kFontIdKey[] = "fontId";
const char kGenericFamilyKey[] = "genericFamily";
const char kLevelOfControlKey[] = "levelOfControl";
const char kDisplayNameKey[] = "displayName";
const char kPixelSizeKey[] = "pixelSize";
const char kScriptKey[] = "script";
const char kSetFromIncognitoError[] =
"Can't modify regular settings from an incognito context.";
// Gets the font name preference path for |generic_family| and |script|. If
// |script| is NULL, uses prefs::kWebKitCommonScript.
std::string GetFontNamePrefPath(fonts::GenericFamily generic_family_enum,
fonts::ScriptCode script_enum) {
// Format is <prefix-(includes-dot)><family>.<script>
std::string result;
size_t prefix_len = strlen(pref_names_util::kWebKitFontPrefPrefix);
std::string generic_family = fonts::ToString(generic_family_enum);
// Script codes are 4, dot adds one more for 5.
result.reserve(prefix_len + generic_family.size() + 5);
result.append(pref_names_util::kWebKitFontPrefPrefix, prefix_len);
result.append(fonts::ToString(generic_family_enum));
result.push_back('.');
const char* script = fonts::ToString(script_enum);
if (script[0] == 0) // Empty string.
result.append(prefs::kWebKitCommonScript);
else
result.append(script);
return result;
}
void MaybeUnlocalizeFontName(std::string* font_name) {
#if BUILDFLAG(IS_WIN)
// Try to get the 'us-en' font name. If it is failing, use the first name
// available.
absl::optional<std::string> localized_font_name =
gfx::win::RetrieveLocalizedFontName(*font_name, "us-en");
if (!localized_font_name)
localized_font_name = gfx::win::RetrieveLocalizedFontName(*font_name, "");
if (localized_font_name)
*font_name = std::move(localized_font_name.value());
#endif // BUILDFLAG(IS_WIN)
}
} // namespace
FontSettingsEventRouter::FontSettingsEventRouter(Profile* profile)
: profile_(profile) {
TRACE_EVENT0("browser,startup", "FontSettingsEventRouter::ctor");
registrar_.Init(profile_->GetPrefs());
// Unretained is safe here because the registrar is owned by this class.
font_change_registrar_.Register(
FontPrefChangeNotifierFactory::GetForProfile(profile),
base::BindRepeating(&FontSettingsEventRouter::OnFontFamilyMapPrefChanged,
base::Unretained(this)));
AddPrefToObserve(prefs::kWebKitDefaultFixedFontSize,
events::FONT_SETTINGS_ON_DEFAULT_FIXED_FONT_SIZE_CHANGED,
fonts::OnDefaultFixedFontSizeChanged::kEventName,
kPixelSizeKey);
AddPrefToObserve(prefs::kWebKitDefaultFontSize,
events::FONT_SETTINGS_ON_DEFAULT_FONT_SIZE_CHANGED,
fonts::OnDefaultFontSizeChanged::kEventName, kPixelSizeKey);
AddPrefToObserve(prefs::kWebKitMinimumFontSize,
events::FONT_SETTINGS_ON_MINIMUM_FONT_SIZE_CHANGED,
fonts::OnMinimumFontSizeChanged::kEventName, kPixelSizeKey);
}
FontSettingsEventRouter::~FontSettingsEventRouter() {}
void FontSettingsEventRouter::AddPrefToObserve(
const char* pref_name,
events::HistogramValue histogram_value,
const char* event_name,
const char* key) {
registrar_.Add(pref_name,
base::BindRepeating(
&FontSettingsEventRouter::OnFontPrefChanged,
base::Unretained(this), histogram_value, event_name, key));
}
void FontSettingsEventRouter::OnFontFamilyMapPrefChanged(
const std::string& pref_name) {
std::string generic_family;
std::string script;
if (pref_names_util::ParseFontNamePrefPath(pref_name, &generic_family,
&script)) {
OnFontNamePrefChanged(pref_name, generic_family, script);
return;
}
NOTREACHED();
}
void FontSettingsEventRouter::OnFontNamePrefChanged(
const std::string& pref_name,
const std::string& generic_family,
const std::string& script) {
const PrefService::Preference* pref = registrar_.prefs()->FindPreference(
pref_name);
CHECK(pref);
if (!pref->GetValue()->is_string()) {
NOTREACHED();
return;
}
std::string font_name = pref->GetValue()->GetString();
base::Value::List args;
base::Value::Dict dict;
dict.Set(kFontIdKey, font_name);
dict.Set(kGenericFamilyKey, generic_family);
dict.Set(kScriptKey, script);
args.Append(std::move(dict));
extensions::preference_helpers::DispatchEventToExtensions(
profile_, events::FONT_SETTINGS_ON_FONT_CHANGED,
fonts::OnFontChanged::kEventName, std::move(args),
extensions::mojom::APIPermissionID::kFontSettings, false, pref_name);
}
void FontSettingsEventRouter::OnFontPrefChanged(
events::HistogramValue histogram_value,
const std::string& event_name,
const std::string& key,
const std::string& pref_name) {
const PrefService::Preference* pref = registrar_.prefs()->FindPreference(
pref_name);
CHECK(pref);
base::Value::List args;
base::Value::Dict dict;
dict.Set(key, pref->GetValue()->Clone());
args.Append(std::move(dict));
extensions::preference_helpers::DispatchEventToExtensions(
profile_, histogram_value, event_name, std::move(args),
extensions::mojom::APIPermissionID::kFontSettings, false, pref_name);
}
FontSettingsAPI::FontSettingsAPI(content::BrowserContext* context)
: font_settings_event_router_(
new FontSettingsEventRouter(Profile::FromBrowserContext(context))) {}
FontSettingsAPI::~FontSettingsAPI() {
}
static base::LazyInstance<BrowserContextKeyedAPIFactory<FontSettingsAPI>>::
DestructorAtExit g_font_settings_api_factory = LAZY_INSTANCE_INITIALIZER;
// static
BrowserContextKeyedAPIFactory<FontSettingsAPI>*
FontSettingsAPI::GetFactoryInstance() {
return g_font_settings_api_factory.Pointer();
}
ExtensionFunction::ResponseAction FontSettingsClearFontFunction::Run() {
Profile* profile = Profile::FromBrowserContext(browser_context());
if (profile->IsOffTheRecord())
return RespondNow(Error(kSetFromIncognitoError));
std::unique_ptr<fonts::ClearFont::Params> params(
fonts::ClearFont::Params::Create(args()));
EXTENSION_FUNCTION_VALIDATE(params.get());
std::string pref_path = GetFontNamePrefPath(params->details.generic_family,
params->details.script);
// Ensure |pref_path| really is for a registered per-script font pref.
EXTENSION_FUNCTION_VALIDATE(profile->GetPrefs()->FindPreference(pref_path));
ExtensionPrefsHelper::Get(profile)->RemoveExtensionControlledPref(
extension_id(), pref_path, kExtensionPrefsScopeRegular);
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction FontSettingsGetFontFunction::Run() {
std::unique_ptr<fonts::GetFont::Params> params(
fonts::GetFont::Params::Create(args()));
EXTENSION_FUNCTION_VALIDATE(params.get());
std::string pref_path = GetFontNamePrefPath(params->details.generic_family,
params->details.script);
Profile* profile = Profile::FromBrowserContext(browser_context());
PrefService* prefs = profile->GetPrefs();
const PrefService::Preference* pref =
prefs->FindPreference(pref_path);
EXTENSION_FUNCTION_VALIDATE(pref && pref->GetValue()->is_string());
std::string font_name = pref->GetValue()->GetString();
// Legacy code was using the localized font name for fontId. These values may
// have been stored in prefs. For backward compatibility, we are converting
// the font name to the unlocalized name.
MaybeUnlocalizeFontName(&font_name);
// We don't support incognito-specific font prefs, so don't consider them when
// getting level of control.
const bool kIncognito = false;
std::string level_of_control =
extensions::preference_helpers::GetLevelOfControl(profile, extension_id(),
pref_path, kIncognito);
base::Value::Dict result;
result.Set(kFontIdKey, font_name);
result.Set(kLevelOfControlKey, level_of_control);
return RespondNow(WithArguments(std::move(result)));
}
ExtensionFunction::ResponseAction FontSettingsSetFontFunction::Run() {
Profile* profile = Profile::FromBrowserContext(browser_context());
if (profile->IsOffTheRecord())
return RespondNow(Error(kSetFromIncognitoError));
std::unique_ptr<fonts::SetFont::Params> params(
fonts::SetFont::Params::Create(args()));
EXTENSION_FUNCTION_VALIDATE(params.get());
std::string pref_path = GetFontNamePrefPath(params->details.generic_family,
params->details.script);
// Ensure |pref_path| really is for a registered font pref.
EXTENSION_FUNCTION_VALIDATE(profile->GetPrefs()->FindPreference(pref_path));
ExtensionPrefsHelper::Get(profile)->SetExtensionControlledPref(
extension_id(), pref_path, kExtensionPrefsScopeRegular,
base::Value(params->details.font_id));
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction FontSettingsGetFontListFunction::Run() {
content::GetFontListAsync(
BindOnce(&FontSettingsGetFontListFunction::FontListHasLoaded, this));
return RespondLater();
}
void FontSettingsGetFontListFunction::FontListHasLoaded(
base::Value::List list) {
ExtensionFunction::ResponseValue response = CopyFontsToResult(list);
Respond(std::move(response));
}
ExtensionFunction::ResponseValue
FontSettingsGetFontListFunction::CopyFontsToResult(
const base::Value::List& fonts) {
base::Value::List result;
for (const auto& entry : fonts) {
if (!entry.is_list()) {
NOTREACHED();
return Error("");
}
const base::Value::List& font_list_value = entry.GetList();
if (font_list_value.size() < 2 || !font_list_value[0].is_string() ||
!font_list_value[1].is_string()) {
NOTREACHED();
return Error("");
}
const std::string& name = font_list_value[0].GetString();
const std::string& localized_name = font_list_value[1].GetString();
base::Value::Dict font_name;
font_name.Set(kFontIdKey, name);
font_name.Set(kDisplayNameKey, localized_name);
result.Append(std::move(font_name));
}
return WithArguments(std::move(result));
}
ExtensionFunction::ResponseAction ClearFontPrefExtensionFunction::Run() {
Profile* profile = Profile::FromBrowserContext(browser_context());
if (profile->IsOffTheRecord())
return RespondNow(Error(kSetFromIncognitoError));
ExtensionPrefsHelper::Get(profile)->RemoveExtensionControlledPref(
extension_id(), GetPrefName(), kExtensionPrefsScopeRegular);
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction GetFontPrefExtensionFunction::Run() {
Profile* profile = Profile::FromBrowserContext(browser_context());
PrefService* prefs = profile->GetPrefs();
const PrefService::Preference* pref = prefs->FindPreference(GetPrefName());
EXTENSION_FUNCTION_VALIDATE(pref);
// We don't support incognito-specific font prefs, so don't consider them when
// getting level of control.
const bool kIncognito = false;
std::string level_of_control =
extensions::preference_helpers::GetLevelOfControl(
profile, extension_id(), GetPrefName(), kIncognito);
base::Value::Dict result;
result.Set(GetKey(), pref->GetValue()->Clone());
result.Set(kLevelOfControlKey, level_of_control);
return RespondNow(WithArguments(std::move(result)));
}
ExtensionFunction::ResponseAction SetFontPrefExtensionFunction::Run() {
Profile* profile = Profile::FromBrowserContext(browser_context());
if (profile->IsOffTheRecord())
return RespondNow(Error(kSetFromIncognitoError));
EXTENSION_FUNCTION_VALIDATE(args().size() >= 1);
EXTENSION_FUNCTION_VALIDATE(args()[0].is_dict());
const base::Value& details = args()[0];
const base::Value* value = details.GetDict().Find(GetKey());
EXTENSION_FUNCTION_VALIDATE(value);
ExtensionPrefsHelper::Get(profile)->SetExtensionControlledPref(
extension_id(), GetPrefName(), kExtensionPrefsScopeRegular,
value->Clone());
return RespondNow(NoArguments());
}
const char* FontSettingsClearDefaultFontSizeFunction::GetPrefName() {
return prefs::kWebKitDefaultFontSize;
}
const char* FontSettingsGetDefaultFontSizeFunction::GetPrefName() {
return prefs::kWebKitDefaultFontSize;
}
const char* FontSettingsGetDefaultFontSizeFunction::GetKey() {
return kPixelSizeKey;
}
const char* FontSettingsSetDefaultFontSizeFunction::GetPrefName() {
return prefs::kWebKitDefaultFontSize;
}
const char* FontSettingsSetDefaultFontSizeFunction::GetKey() {
return kPixelSizeKey;
}
const char* FontSettingsClearDefaultFixedFontSizeFunction::GetPrefName() {
return prefs::kWebKitDefaultFixedFontSize;
}
const char* FontSettingsGetDefaultFixedFontSizeFunction::GetPrefName() {
return prefs::kWebKitDefaultFixedFontSize;
}
const char* FontSettingsGetDefaultFixedFontSizeFunction::GetKey() {
return kPixelSizeKey;
}
const char* FontSettingsSetDefaultFixedFontSizeFunction::GetPrefName() {
return prefs::kWebKitDefaultFixedFontSize;
}
const char* FontSettingsSetDefaultFixedFontSizeFunction::GetKey() {
return kPixelSizeKey;
}
const char* FontSettingsClearMinimumFontSizeFunction::GetPrefName() {
return prefs::kWebKitMinimumFontSize;
}
const char* FontSettingsGetMinimumFontSizeFunction::GetPrefName() {
return prefs::kWebKitMinimumFontSize;
}
const char* FontSettingsGetMinimumFontSizeFunction::GetKey() {
return kPixelSizeKey;
}
const char* FontSettingsSetMinimumFontSizeFunction::GetPrefName() {
return prefs::kWebKitMinimumFontSize;
}
const char* FontSettingsSetMinimumFontSizeFunction::GetKey() {
return kPixelSizeKey;
}
} // namespace extensions