blob: 128853a21d6e1c8e82ebc20fb354f48ac6b10fb8 [file] [log] [blame]
/*
* Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/platform/fonts/font_cache.h"
#include <limits>
#include <memory>
#include "base/debug/alias.h"
#include "base/feature_list.h"
#include "base/notreached.h"
#include "base/strings/escape.h"
#include "base/system/sys_info.h"
#include "base/timer/elapsed_timer.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "skia/ext/font_utils.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/font_family_names.h"
#include "third_party/blink/renderer/platform/fonts/alternate_font_family.h"
#include "third_party/blink/renderer/platform/fonts/font_cache_client.h"
#include "third_party/blink/renderer/platform/fonts/font_data_cache.h"
#include "third_party/blink/renderer/platform/fonts/font_description.h"
#include "third_party/blink/renderer/platform/fonts/font_fallback_map.h"
#include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
#include "third_party/blink/renderer/platform/fonts/font_global_context.h"
#include "third_party/blink/renderer/platform/fonts/font_performance.h"
#include "third_party/blink/renderer/platform/fonts/font_platform_data_cache.h"
#include "third_party/blink/renderer/platform/fonts/font_unique_name_lookup.h"
#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
#include "third_party/blink/renderer/platform/heap/thread_state.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
#include "third_party/blink/renderer/platform/json/json_parser.h"
#include "third_party/blink/renderer/platform/json/json_values.h"
#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
#include "third_party/blink/renderer/platform/wtf/text/code_point_iterator.h"
#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
#include "ui/gfx/font_list.h"
#if BUILDFLAG(IS_WIN)
#include "third_party/skia/include/ports/SkTypeface_win.h"
#endif
namespace blink {
const char kColorEmojiLocale[] = "und-Zsye";
const char kMonoEmojiLocale[] = "und-Zsym";
#if BUILDFLAG(IS_ANDROID)
extern const char kNotoColorEmojiCompat[] = "Noto Color Emoji Compat";
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
float FontCache::device_scale_factor_ = 1.0;
#endif
#if BUILDFLAG(IS_WIN)
bool FontCache::antialiased_text_enabled_ = false;
bool FontCache::lcd_text_enabled_ = false;
#endif // BUILDFLAG(IS_WIN)
FontCache& FontCache::Get() {
return FontGlobalContext::GetFontCache();
}
FontCache::FontCache()
: font_manager_(sk_ref_sp(skia::DefaultFontMgr().get())) {
DCHECK(font_manager_.get());
}
FontCache::~FontCache() = default;
void FontCache::Trace(Visitor* visitor) const {
visitor->Trace(font_cache_clients_);
visitor->Trace(font_platform_data_cache_);
visitor->Trace(fallback_list_shaper_cache_);
visitor->Trace(font_data_cache_);
visitor->Trace(font_fallback_map_);
#if BUILDFLAG(IS_MAC)
visitor->Trace(character_fallback_cache_);
#endif
}
#if !BUILDFLAG(IS_MAC)
const FontPlatformData* FontCache::SystemFontPlatformData(
const FontDescription& font_description) {
const AtomicString& family = FontCache::SystemFontFamily();
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA) || \
BUILDFLAG(IS_IOS)
if (family.empty() || family == font_family_names::kSystemUi)
return nullptr;
#else
DCHECK(!family.empty() && family != font_family_names::kSystemUi);
#endif
return GetFontPlatformData(font_description, FontFaceCreationParams(family),
AlternateFontName::kNoAlternate);
}
#endif
const FontPlatformData* FontCache::GetFontPlatformData(
const FontDescription& font_description,
const FontFaceCreationParams& creation_params,
AlternateFontName alternate_font_name) {
TRACE_EVENT0("fonts", "FontCache::GetFontPlatformData");
if (!platform_init_) {
platform_init_ = true;
PlatformInit();
}
#if !BUILDFLAG(IS_MAC)
if (creation_params.CreationType() == kCreateFontByFamily &&
creation_params.Family() == font_family_names::kSystemUi) {
return SystemFontPlatformData(font_description);
}
#endif
return font_platform_data_cache_.GetOrCreateFontPlatformData(
this, font_description, creation_params, alternate_font_name);
}
ShapeCache* FontCache::GetShapeCache(const FallbackListCompositeKey& key) {
auto result = fallback_list_shaper_cache_.insert(key, nullptr);
if (result.is_new_entry) {
result.stored_value->value = MakeGarbageCollected<ShapeCache>();
}
return result.stored_value->value.Get();
}
void FontCache::AcceptLanguagesChanged(const String& accept_languages) {
LayoutLocale::AcceptLanguagesChanged(accept_languages);
Get().InvalidateShapeCache();
}
const SimpleFontData* FontCache::GetFontData(
const FontDescription& font_description,
const AtomicString& family,
AlternateFontName altername_font_name) {
if (const FontPlatformData* platform_data = GetFontPlatformData(
font_description,
FontFaceCreationParams(
AdjustFamilyNameToAvoidUnsupportedFonts(family)),
altername_font_name)) {
return FontDataFromFontPlatformData(
platform_data, font_description.SubpixelAscentDescent());
}
return nullptr;
}
const SimpleFontData* FontCache::FontDataFromFontPlatformData(
const FontPlatformData* platform_data,
bool subpixel_ascent_descent) {
return font_data_cache_.Get(platform_data, subpixel_ascent_descent);
}
bool FontCache::IsPlatformFamilyMatchAvailable(
const FontDescription& font_description,
const AtomicString& family) {
return GetFontPlatformData(
font_description,
FontFaceCreationParams(AdjustFamilyNameToAvoidUnsupportedFonts(family)),
AlternateFontName::kNoAlternate);
}
bool FontCache::IsPlatformFontUniqueNameMatchAvailable(
const FontDescription& font_description,
const AtomicString& unique_font_name) {
// Return early to avoid attempting fallback.
if (unique_font_name.empty()) {
return false;
}
return GetFontPlatformData(font_description,
FontFaceCreationParams(unique_font_name),
AlternateFontName::kLocalUniqueFace);
}
String FontCache::FirstAvailableOrFirst(const String& families) {
// The conversions involve at least two string copies, and more if non-ASCII.
// For now we prefer shared code over the cost because a) inputs are
// only from grd/xtb and all ASCII, and b) at most only a few times per
// setting change/script.
return String::FromUTF8(
gfx::FontList::FirstAvailableOrFirst(families.Utf8().c_str()));
}
const SimpleFontData* FontCache::FallbackFontForCharacter(
const FontDescription& description,
UChar32 lookup_char,
const SimpleFontData* font_data_to_substitute,
FontFallbackPriority fallback_priority) {
TRACE_EVENT0("fonts", "FontCache::FallbackFontForCharacter");
// In addition to PUA, do not perform fallback for non-characters either. Some
// of these are sentinel characters to detect encodings and do appear on
// websites. More details on
// http://www.unicode.org/faq/private_use.html#nonchar1 - See also
// crbug.com/862352 where performing fallback for U+FFFE causes a memory
// regression.
if (Character::IsPrivateUse(lookup_char) ||
Character::IsNonCharacter(lookup_char))
return nullptr;
base::ElapsedTimer timer;
const SimpleFontData* result = PlatformFallbackFontForCharacter(
description, lookup_char, font_data_to_substitute, fallback_priority);
FontPerformance::AddSystemFallbackFontTime(timer.Elapsed());
return result;
}
void FontCache::PurgeFallbackListShaperCache() {
TRACE_EVENT0("fonts,ui", "FontCache::PurgeFallbackListShaperCache");
for (auto& shape_cache : fallback_list_shaper_cache_.Values()) {
shape_cache->Clear();
}
}
void FontCache::InvalidateShapeCache() {
PurgeFallbackListShaperCache();
}
void FontCache::Purge() {
// Ideally we should never be forcing the purge while the
// FontCachePurgePreventer is in scope, but we call purge() at any timing
// via MemoryPressureListenerRegistry.
if (purge_prevent_count_)
return;
PurgeFallbackListShaperCache();
}
void FontCache::AddClient(FontCacheClient* client) {
CHECK(client);
DCHECK(!font_cache_clients_.Contains(client));
font_cache_clients_.insert(client);
}
uint16_t FontCache::Generation() {
return generation_;
}
void FontCache::Invalidate() {
TRACE_EVENT0("fonts,ui", "FontCache::Invalidate");
font_platform_data_cache_.Clear();
font_data_cache_.Clear();
generation_++;
for (const auto& client : font_cache_clients_) {
client->FontCacheInvalidated();
}
Purge();
}
void FontCache::CrashWithFontInfo(const FontDescription* font_description) {
SkFontMgr* font_mgr = nullptr;
int num_families = std::numeric_limits<int>::min();
bool is_test_font_mgr = false;
if (FontGlobalContext::TryGet()) {
FontCache& font_cache = FontGlobalContext::GetFontCache();
#if BUILDFLAG(IS_WIN)
is_test_font_mgr = font_cache.is_test_font_mgr_;
#endif
num_families = font_cache.font_manager_->countFamilies();
}
// In production, these 2 font managers must match.
// They don't match in unit tests or in single process mode.
SkFontMgr* skia_default_font_mgr = skia::DefaultFontMgr().get();
base::debug::Alias(&font_mgr);
base::debug::Alias(&skia_default_font_mgr);
FontDescription font_description_copy = *font_description;
base::debug::Alias(&font_description_copy);
base::debug::Alias(&is_test_font_mgr);
base::debug::Alias(&num_families);
NOTREACHED();
}
void FontCache::DumpShapeResultCache(
base::trace_event::ProcessMemoryDump* memory_dump) {
DCHECK(IsMainThread());
base::trace_event::MemoryAllocatorDump* dump =
memory_dump->CreateAllocatorDump("font_caches/shape_caches");
size_t shape_result_cache_size = 0;
for (const auto& shape_cache : fallback_list_shaper_cache_.Values()) {
shape_result_cache_size += shape_cache->ByteSize();
}
dump->AddScalar("size", "bytes", shape_result_cache_size);
memory_dump->AddSuballocation(dump->guid(),
Partitions::kAllocatedObjectPoolName);
}
sk_sp<SkTypeface> FontCache::CreateTypefaceFromUniqueName(
const FontFaceCreationParams& creation_params) {
FontUniqueNameLookup* unique_name_lookup =
FontGlobalContext::Get().GetFontUniqueNameLookup();
DCHECK(unique_name_lookup);
sk_sp<SkTypeface> uniquely_identified_font =
unique_name_lookup->MatchUniqueName(creation_params.Family());
if (uniquely_identified_font) {
return uniquely_identified_font;
}
return nullptr;
}
// static
FontCache::Bcp47Vector FontCache::GetBcp47LocaleForRequest(
const FontDescription& font_description,
FontFallbackPriority fallback_priority) {
Bcp47Vector result;
// Fill in the list of locales in the reverse priority order.
// Skia expects the highest array index to be the first priority.
const LayoutLocale* content_locale = font_description.Locale();
if (const LayoutLocale* han_locale =
LayoutLocale::LocaleForHan(content_locale)) {
result.push_back(han_locale->LocaleForHanForSkFontMgr());
}
result.push_back(LayoutLocale::GetDefault().LocaleForSkFontMgr());
if (content_locale)
result.push_back(content_locale->LocaleForSkFontMgr());
if (IsEmojiPresentationEmoji(fallback_priority)) {
result.push_back(kColorEmojiLocale);
} else if (IsTextPresentationEmoji(fallback_priority)) {
result.push_back(kMonoEmojiLocale);
}
return result;
}
// TODO(crbug/342967843): In WebTest, Fuchsia initializes fonts by calling
// `skia::InitializeSkFontMgrForTest();` expecting that other code doesn't
// initialize SkFontMgr beforehand. But `FontCache::MaybePreloadSystemFonts()`
// breaks this expectation. So we don't provide
// `FontCache::MaybePreloadSystemFonts()` feature for Fuchsia for now.
#if BUILDFLAG(IS_FUCHSIA)
// static
void FontCache::MaybePreloadSystemFonts() {}
#else
// static
void FontCache::MaybePreloadSystemFonts() {
static bool initialized = false;
if (initialized) {
return;
}
initialized = true;
CHECK(IsMainThread());
if (!base::FeatureList::IsEnabled(features::kPreloadSystemFonts)) {
return;
}
if (base::SysInfo::AmountOfPhysicalMemory().InGiB() <
features::kPreloadSystemFontsRequiredMemoryGB.Get()) {
return;
}
std::unique_ptr<JSONArray> targets =
JSONArray::From(ParseJSON(String::FromUTF8(
base::UnescapeURLComponent(features::kPreloadSystemFontsTargets.Get(),
base::UnescapeRule::SPACES))));
if (!targets) {
return;
}
const LayoutLocale& locale = LayoutLocale::GetDefault();
for (wtf_size_t i = 0; i < targets->size(); ++i) {
JSONObject* target = JSONObject::Cast(targets->at(i));
bool success = true;
String family;
success &= target->GetString("family", &family);
int weight;
success &= target->GetInteger("weight", &weight);
double specified_size;
success &= target->GetDouble("size", &specified_size);
double computed_size;
success &= target->GetDouble("csize", &computed_size);
String text;
success &= target->GetString("text", &text);
if (success) {
TRACE_EVENT("fonts", "PreloadSystemFonts", "family", family, "weight",
weight, "specified_size", specified_size, "computed_size",
computed_size, "text", text);
FontDescription font_description;
const AtomicString family_atomic_string(family);
FontFamily font_family(family_atomic_string,
FontFamily::Type::kFamilyName);
font_description.SetFamily(font_family);
font_description.SetWeight(FontSelectionValue(weight));
font_description.SetLocale(&locale);
font_description.SetSpecifiedSize(
base::saturated_cast<float>(specified_size));
font_description.SetComputedSize(
base::saturated_cast<float>(computed_size));
font_description.SetGenericFamily(FontDescription::kSansSerifFamily);
const SimpleFontData* simple_font_data =
FontCache::Get().GetFontData(font_description, AtomicString(family));
if (simple_font_data) {
for (UChar32 c : text) {
Glyph glyph = simple_font_data->GlyphForCharacter(c);
std::ignore = simple_font_data->BoundsForGlyph(glyph);
}
}
}
}
}
#endif // BUILDFLAG(IS_FUCHSIA)
FontFallbackMap& FontCache::GetFontFallbackMap() {
if (!font_fallback_map_) {
font_fallback_map_ = MakeGarbageCollected<FontFallbackMap>(nullptr);
AddClient(font_fallback_map_);
}
return *font_fallback_map_;
}
} // namespace blink