blob: a9740b49cfcfcc528f13d2f767d0451f9be6e0a7 [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/memory/ptr_util.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.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_cache_key.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_global_context.h"
#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
#include "third_party/blink/renderer/platform/fonts/font_smoothing_mode.h"
#include "third_party/blink/renderer/platform/fonts/font_unique_name_lookup.h"
#include "third_party/blink/renderer/platform/fonts/shaping/shape_cache.h"
#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
#include "third_party/blink/renderer/platform/fonts/text_rendering_mode.h"
#include "third_party/blink/renderer/platform/histogram.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/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/text/layout_locale.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.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"
namespace blink {
SkFontMgr* FontCache::static_font_manager_ = nullptr;
#if defined(OS_LINUX) || defined(OS_CHROMEOS)
float FontCache::device_scale_factor_ = 1.0;
#endif
#if defined(OS_WIN)
bool FontCache::antialiased_text_enabled_ = false;
bool FontCache::lcd_text_enabled_ = false;
bool FontCache::use_skia_font_fallback_ = false;
#endif // defined(OS_WIN)
FontCache* FontCache::GetFontCache() {
return &FontGlobalContext::GetFontCache();
}
#if !defined(OS_WIN)
FontCache::FontCache()
: purge_prevent_count_(0), font_manager_(sk_ref_sp(static_font_manager_)) {}
#endif // !defined(OS_WIN) && !defined(OS_LINUX)
#if !defined(OS_MACOSX)
FontPlatformData* FontCache::SystemFontPlatformData(
const FontDescription& font_description) {
const AtomicString& family = FontCache::SystemFontFamily();
#if defined(OS_LINUX)
if (family.IsEmpty() || family == font_family_names::kSystemUi)
return nullptr;
#else
DCHECK(!family.IsEmpty() && family != font_family_names::kSystemUi);
#endif
return GetFontPlatformData(font_description, FontFaceCreationParams(family),
AlternateFontName::kNoAlternate);
}
#endif
FontPlatformData* FontCache::GetFontPlatformData(
const FontDescription& font_description,
const FontFaceCreationParams& creation_params,
AlternateFontName alternate_font_name) {
if (!platform_init_) {
platform_init_ = true;
PlatformInit();
}
#if !defined(OS_MACOSX)
if (creation_params.CreationType() == kCreateFontByFamily &&
creation_params.Family() == font_family_names::kSystemUi) {
return SystemFontPlatformData(font_description);
}
#endif
float size = font_description.EffectiveFontSize();
unsigned rounded_size = size * FontCacheKey::PrecisionMultiplier();
bool is_unique_match =
alternate_font_name == AlternateFontName::kLocalUniqueFace;
FontCacheKey key =
font_description.CacheKey(creation_params, is_unique_match);
// Remove the font size from the cache key, and handle the font size
// separately in the inner HashMap. So that different size of FontPlatformData
// can share underlying SkTypeface.
if (RuntimeEnabledFeatures::FontCacheScalingEnabled())
key.ClearFontSize();
FontPlatformData* result;
bool found_result;
{
// addResult's scope must end before we recurse for alternate family names
// below, to avoid triggering its dtor hash-changed asserts.
SizedFontPlatformDataSet* sized_fonts =
&font_platform_data_cache_.insert(key, SizedFontPlatformDataSet())
.stored_value->value;
bool was_empty = sized_fonts->IsEmpty();
// Take a different size instance of the same font before adding an entry to
// |sizedFont|.
FontPlatformData* another_size =
was_empty ? nullptr : sized_fonts->begin()->value.get();
auto add_result = sized_fonts->insert(rounded_size, nullptr);
std::unique_ptr<FontPlatformData>* found = &add_result.stored_value->value;
if (add_result.is_new_entry) {
if (was_empty) {
*found = CreateFontPlatformData(font_description, creation_params, size,
alternate_font_name);
} else if (another_size) {
*found = ScaleFontPlatformData(*another_size, font_description,
creation_params, size);
}
}
result = found->get();
found_result = result || !add_result.is_new_entry;
}
if (!found_result &&
alternate_font_name == AlternateFontName::kAllowAlternate &&
creation_params.CreationType() == kCreateFontByFamily) {
// We were unable to find a font. We have a small set of fonts that we alias
// to other names, e.g., Arial/Helvetica, Courier/Courier New, etc. Try
// looking up the font under the aliased name.
const AtomicString& alternate_name =
AlternateFamilyName(creation_params.Family());
if (!alternate_name.IsEmpty()) {
FontFaceCreationParams create_by_alternate_family(alternate_name);
result = GetFontPlatformData(font_description, create_by_alternate_family,
AlternateFontName::kNoAlternate);
}
if (result) {
// Cache the result under the old name.
auto* adding =
&font_platform_data_cache_.insert(key, SizedFontPlatformDataSet())
.stored_value->value;
adding->Set(rounded_size, std::make_unique<FontPlatformData>(*result));
}
}
return result;
}
std::unique_ptr<FontPlatformData> FontCache::ScaleFontPlatformData(
const FontPlatformData& font_platform_data,
const FontDescription& font_description,
const FontFaceCreationParams& creation_params,
float font_size) {
TRACE_EVENT0("ui", "FontCache::ScaleFontPlatformData");
#if defined(OS_MACOSX)
return CreateFontPlatformData(font_description, creation_params, font_size);
#else
return std::make_unique<FontPlatformData>(font_platform_data, font_size);
#endif
}
ShapeCache* FontCache::GetShapeCache(const FallbackListCompositeKey& key) {
FallbackListShaperCache::iterator it = fallback_list_shaper_cache_.find(key);
ShapeCache* result = nullptr;
if (it == fallback_list_shaper_cache_.end()) {
result = new ShapeCache();
fallback_list_shaper_cache_.Set(key, base::WrapUnique(result));
} else {
result = it->value.get();
}
DCHECK(result);
return result;
}
void FontCache::SetFontManager(sk_sp<SkFontMgr> font_manager) {
DCHECK(!static_font_manager_);
static_font_manager_ = font_manager.release();
}
void FontCache::AcceptLanguagesChanged(const String& accept_languages) {
LayoutLocale::AcceptLanguagesChanged(accept_languages);
GetFontCache()->InvalidateShapeCache();
}
scoped_refptr<SimpleFontData> FontCache::GetFontData(
const FontDescription& font_description,
const AtomicString& family,
AlternateFontName altername_font_name,
ShouldRetain should_retain) {
if (FontPlatformData* platform_data = GetFontPlatformData(
font_description,
FontFaceCreationParams(
AdjustFamilyNameToAvoidUnsupportedFonts(family)),
altername_font_name)) {
return FontDataFromFontPlatformData(
platform_data, should_retain, font_description.SubpixelAscentDescent());
}
return nullptr;
}
scoped_refptr<SimpleFontData> FontCache::FontDataFromFontPlatformData(
const FontPlatformData* platform_data,
ShouldRetain should_retain,
bool subpixel_ascent_descent) {
#if DCHECK_IS_ON()
if (should_retain == kDoNotRetain)
DCHECK(purge_prevent_count_);
#endif
return font_data_cache_.Get(platform_data, should_retain,
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 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().data()).c_str());
}
SimpleFontData* FontCache::GetNonRetainedLastResortFallbackFont(
const FontDescription& font_description) {
auto font = GetLastResortFallbackFont(font_description, kDoNotRetain);
if (font)
font->AddRef();
return font.get();
}
scoped_refptr<SimpleFontData> FontCache::FallbackFontForCharacter(
const FontDescription& description,
UChar32 lookup_char,
const SimpleFontData* font_data_to_substitute,
FontFallbackPriority fallback_priority) {
// 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;
return PlatformFallbackFontForCharacter(
description, lookup_char, font_data_to_substitute, fallback_priority);
}
void FontCache::ReleaseFontData(const SimpleFontData* font_data) {
font_data_cache_.Release(font_data);
}
void FontCache::PurgePlatformFontDataCache() {
TRACE_EVENT0("ui", "FontCache::PurgePlatformFontDataCache");
Vector<FontCacheKey> keys_to_remove;
keys_to_remove.ReserveInitialCapacity(font_platform_data_cache_.size());
for (auto& sized_fonts : font_platform_data_cache_) {
Vector<unsigned> sizes_to_remove;
sizes_to_remove.ReserveInitialCapacity(sized_fonts.value.size());
for (const auto& platform_data : sized_fonts.value) {
if (platform_data.value &&
!font_data_cache_.Contains(platform_data.value.get()))
sizes_to_remove.push_back(platform_data.key);
}
sized_fonts.value.RemoveAll(sizes_to_remove);
if (sized_fonts.value.IsEmpty())
keys_to_remove.push_back(sized_fonts.key);
}
font_platform_data_cache_.RemoveAll(keys_to_remove);
}
void FontCache::PurgeFallbackListShaperCache() {
TRACE_EVENT0("ui", "FontCache::PurgeFallbackListShaperCache");
unsigned items = 0;
FallbackListShaperCache::iterator iter;
for (iter = fallback_list_shaper_cache_.begin();
iter != fallback_list_shaper_cache_.end(); ++iter) {
items += iter->value->size();
}
fallback_list_shaper_cache_.clear();
DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, shape_cache_histogram,
("Blink.Fonts.ShapeCache", 1, 1000000, 50));
shape_cache_histogram.Count(items);
}
void FontCache::InvalidateShapeCache() {
PurgeFallbackListShaperCache();
}
void FontCache::Purge(PurgeSeverity purge_severity) {
// 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;
if (!font_data_cache_.Purge(purge_severity))
return;
PurgePlatformFontDataCache();
PurgeFallbackListShaperCache();
}
void FontCache::AddClient(FontCacheClient* client) {
CHECK(client);
if (!font_cache_clients_) {
font_cache_clients_ =
MakeGarbageCollected<HeapHashSet<WeakMember<FontCacheClient>>>();
font_cache_clients_.RegisterAsStaticReference();
}
DCHECK(!font_cache_clients_->Contains(client));
font_cache_clients_->insert(client);
}
uint16_t FontCache::Generation() {
return generation_;
}
void FontCache::Invalidate() {
TRACE_EVENT0("ui", "FontCache::Invalidate");
font_platform_data_cache_.clear();
generation_++;
if (font_cache_clients_) {
for (const auto& client : *font_cache_clients_)
client->FontCacheInvalidated();
}
Purge(kForcePurge);
}
void FontCache::CrashWithFontInfo(const FontDescription* font_description) {
FontCache* font_cache = nullptr;
SkFontMgr* font_mgr = nullptr;
int num_families = std::numeric_limits<int>::min();
bool is_test_font_mgr = false;
if (FontGlobalContext::Get(kDoNotCreate)) {
font_cache = FontCache::GetFontCache();
if (font_cache) {
#if defined(OS_WIN)
is_test_font_mgr = font_cache->is_test_font_mgr_;
#endif
font_mgr = font_cache->font_manager_.get();
if (font_mgr)
num_families = font_mgr->countFamilies();
}
}
// In production, these 3 font managers must match.
// They don't match in unit tests or in single process mode.
SkFontMgr* static_font_mgr = static_font_manager_;
SkFontMgr* skia_default_font_mgr = SkFontMgr::RefDefault().get();
base::debug::Alias(&font_mgr);
base::debug::Alias(&static_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);
CHECK(false);
}
void FontCache::DumpFontPlatformDataCache(
base::trace_event::ProcessMemoryDump* memory_dump) {
DCHECK(IsMainThread());
base::trace_event::MemoryAllocatorDump* dump =
memory_dump->CreateAllocatorDump("font_caches/font_platform_data_cache");
size_t font_platform_data_objects_size =
font_platform_data_cache_.size() * sizeof(FontPlatformData);
dump->AddScalar("size", "bytes", font_platform_data_objects_size);
memory_dump->AddSuballocation(dump->guid(),
WTF::Partitions::kAllocatedObjectPoolName);
}
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;
FallbackListShaperCache::iterator iter;
for (iter = fallback_list_shaper_cache_.begin();
iter != fallback_list_shaper_cache_.end(); ++iter) {
shape_result_cache_size += iter->value->ByteSize();
}
dump->AddScalar("size", "bytes", shape_result_cache_size);
memory_dump->AddSuballocation(dump->guid(),
WTF::Partitions::kAllocatedObjectPoolName);
}
sk_sp<SkTypeface> FontCache::CreateTypefaceFromUniqueName(
const FontFaceCreationParams& creation_params,
CString& name) {
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;
}
} // namespace blink