blob: 6153149d2623d4d3dc7d27d45a5878d5af12fcb9 [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 "platform/fonts/FontCache.h"
#include <memory>
#include "base/trace_event/process_memory_dump.h"
#include "platform/FontFamilyNames.h"
#include "platform/Histogram.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/fonts/AcceptLanguagesResolver.h"
#include "platform/fonts/AlternateFontFamily.h"
#include "platform/fonts/FontCacheClient.h"
#include "platform/fonts/FontCacheKey.h"
#include "platform/fonts/FontDataCache.h"
#include "platform/fonts/FontDescription.h"
#include "platform/fonts/FontPlatformData.h"
#include "platform/fonts/FontSmoothingMode.h"
#include "platform/fonts/SimpleFontData.h"
#include "platform/fonts/TextRenderingMode.h"
#include "platform/fonts/opentype/OpenTypeVerticalData.h"
#include "platform/fonts/shaping/ShapeCache.h"
#include "platform/instrumentation/tracing/web_memory_allocator_dump.h"
#include "platform/instrumentation/tracing/web_process_memory_dump.h"
#include "platform/wtf/HashMap.h"
#include "platform/wtf/ListHashSet.h"
#include "platform/wtf/PtrUtil.h"
#include "platform/wtf/StdLibExtras.h"
#include "platform/wtf/Vector.h"
#include "platform/wtf/debug/Alias.h"
#include "platform/wtf/text/AtomicStringHash.h"
#include "platform/wtf/text/StringHash.h"
#include "public/platform/Platform.h"
#include "ui/gfx/font_list.h"
using namespace WTF;
namespace blink {
#if !OS(WIN) && !OS(LINUX)
FontCache::FontCache() : purge_prevent_count_(0), font_manager_(nullptr) {}
#endif // !OS(WIN) && !OS(LINUX)
typedef HashMap<unsigned,
std::unique_ptr<FontPlatformData>,
WTF::IntHash<unsigned>,
WTF::UnsignedWithZeroKeyHashTraits<unsigned>>
SizedFontPlatformDataSet;
typedef HashMap<FontCacheKey,
SizedFontPlatformDataSet,
FontCacheKeyHash,
FontCacheKeyTraits>
FontPlatformDataCache;
typedef HashMap<FallbackListCompositeKey,
std::unique_ptr<ShapeCache>,
FallbackListCompositeKeyHash,
FallbackListCompositeKeyTraits>
FallbackListShaperCache;
static FontPlatformDataCache* g_font_platform_data_cache = nullptr;
static FallbackListShaperCache* g_fallback_list_shaper_cache = nullptr;
SkFontMgr* FontCache::static_font_manager_ = nullptr;
#if OS(WIN)
bool FontCache::antialiased_text_enabled_ = false;
bool FontCache::lcd_text_enabled_ = false;
float FontCache::device_scale_factor_ = 1.0;
bool FontCache::use_skia_font_fallback_ = false;
#endif // OS(WIN)
FontCache* FontCache::GetFontCache() {
DEFINE_STATIC_LOCAL(FontCache, global_font_cache, ());
return &global_font_cache;
}
#if !OS(MACOSX)
FontPlatformData* FontCache::SystemFontPlatformData(
const FontDescription& font_description) {
const AtomicString& family = FontCache::SystemFontFamily();
#if OS(LINUX)
if (family.IsEmpty() || family == FontFamilyNames::system_ui)
return nullptr;
#else
DCHECK(!family.IsEmpty() && family != FontFamilyNames::system_ui);
#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 (!g_font_platform_data_cache) {
g_font_platform_data_cache = new FontPlatformDataCache;
PlatformInit();
}
#if !OS(MACOSX)
if (creation_params.CreationType() == kCreateFontByFamily &&
creation_params.Family() == FontFamilyNames::system_ui) {
return SystemFontPlatformData(font_description);
}
#endif
float size = font_description.EffectiveFontSize();
unsigned rounded_size = size * FontCacheKey::PrecisionMultiplier();
FontCacheKey key = font_description.CacheKey(creation_params);
// 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 trigering its dtor hash-changed asserts.
SizedFontPlatformDataSet* sized_fonts =
&g_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 =
&g_font_platform_data_cache->insert(key, SizedFontPlatformDataSet())
.stored_value->value;
adding->Set(rounded_size, WTF::WrapUnique(new 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) {
#if OS(MACOSX)
return CreateFontPlatformData(font_description, creation_params, font_size);
#else
return WTF::MakeUnique<FontPlatformData>(font_platform_data, font_size);
#endif
}
ShapeCache* FontCache::GetShapeCache(const FallbackListCompositeKey& key) {
if (!g_fallback_list_shaper_cache)
g_fallback_list_shaper_cache = new FallbackListShaperCache;
FallbackListShaperCache::iterator it =
g_fallback_list_shaper_cache->find(key);
ShapeCache* result = nullptr;
if (it == g_fallback_list_shaper_cache->end()) {
result = new ShapeCache();
g_fallback_list_shaper_cache->Set(key, WTF::WrapUnique(result));
} else {
result = it->value.get();
}
DCHECK(result);
return result;
}
typedef HashMap<FontCache::FontFileKey,
RefPtr<OpenTypeVerticalData>,
IntHash<FontCache::FontFileKey>,
UnsignedWithZeroKeyHashTraits<FontCache::FontFileKey>>
FontVerticalDataCache;
FontVerticalDataCache& FontVerticalDataCacheInstance() {
DEFINE_STATIC_LOCAL(FontVerticalDataCache, font_vertical_data_cache, ());
return font_vertical_data_cache;
}
void FontCache::SetFontManager(sk_sp<SkFontMgr> font_manager) {
DCHECK(!static_font_manager_);
static_font_manager_ = font_manager.release();
}
PassRefPtr<OpenTypeVerticalData> FontCache::GetVerticalData(
const FontFileKey& key,
const FontPlatformData& platform_data) {
FontVerticalDataCache& font_vertical_data_cache =
FontVerticalDataCacheInstance();
FontVerticalDataCache::iterator result = font_vertical_data_cache.find(key);
if (result != font_vertical_data_cache.end())
return result.Get()->value;
RefPtr<OpenTypeVerticalData> vertical_data =
OpenTypeVerticalData::Create(platform_data);
if (!vertical_data->IsOpenType())
vertical_data.Clear();
font_vertical_data_cache.Set(key, vertical_data);
return vertical_data;
}
void FontCache::AcceptLanguagesChanged(const String& accept_languages) {
AcceptLanguagesResolver::AcceptLanguagesChanged(accept_languages);
GetFontCache()->InvalidateShapeCache();
}
static FontDataCache* g_font_data_cache = 0;
PassRefPtr<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;
}
PassRefPtr<SimpleFontData> FontCache::FontDataFromFontPlatformData(
const FontPlatformData* platform_data,
ShouldRetain should_retain,
bool subpixel_ascent_descent) {
if (!g_font_data_cache)
g_font_data_cache = new FontDataCache;
#if DCHECK_IS_ON()
if (should_retain == kDoNotRetain)
DCHECK(purge_prevent_count_);
#endif
return g_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) {
return GetLastResortFallbackFont(font_description, kDoNotRetain).LeakRef();
}
void FontCache::ReleaseFontData(const SimpleFontData* font_data) {
DCHECK(g_font_data_cache);
g_font_data_cache->Release(font_data);
}
static inline void PurgePlatformFontDataCache() {
if (!g_font_platform_data_cache)
return;
Vector<FontCacheKey> keys_to_remove;
keys_to_remove.ReserveInitialCapacity(g_font_platform_data_cache->size());
for (auto& sized_fonts : *g_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 &&
!g_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);
}
g_font_platform_data_cache->RemoveAll(keys_to_remove);
}
static inline void PurgeFontVerticalDataCache() {
FontVerticalDataCache& font_vertical_data_cache =
FontVerticalDataCacheInstance();
if (!font_vertical_data_cache.IsEmpty()) {
// Mark & sweep unused verticalData
FontVerticalDataCache::iterator vertical_data_end =
font_vertical_data_cache.end();
for (FontVerticalDataCache::iterator vertical_data =
font_vertical_data_cache.begin();
vertical_data != vertical_data_end; ++vertical_data) {
if (vertical_data->value)
vertical_data->value->SetInFontCache(false);
}
g_font_data_cache->MarkAllVerticalData();
Vector<FontCache::FontFileKey> keys_to_remove;
keys_to_remove.ReserveInitialCapacity(font_vertical_data_cache.size());
for (FontVerticalDataCache::iterator vertical_data =
font_vertical_data_cache.begin();
vertical_data != vertical_data_end; ++vertical_data) {
if (!vertical_data->value || !vertical_data->value->InFontCache())
keys_to_remove.push_back(vertical_data->key);
}
font_vertical_data_cache.RemoveAll(keys_to_remove);
}
}
static inline void PurgeFallbackListShaperCache() {
unsigned items = 0;
if (g_fallback_list_shaper_cache) {
FallbackListShaperCache::iterator iter;
for (iter = g_fallback_list_shaper_cache->begin();
iter != g_fallback_list_shaper_cache->end(); ++iter) {
items += iter->value->size();
}
g_fallback_list_shaper_cache->clear();
}
DEFINE_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 MemoryCoordinator.
if (purge_prevent_count_)
return;
if (!g_font_data_cache || !g_font_data_cache->Purge(purge_severity))
return;
PurgePlatformFontDataCache();
PurgeFontVerticalDataCache();
PurgeFallbackListShaperCache();
}
static bool g_invalidate_font_cache = false;
HeapHashSet<WeakMember<FontCacheClient>>& FontCacheClients() {
DEFINE_STATIC_LOCAL(HeapHashSet<WeakMember<FontCacheClient>>, clients,
(new HeapHashSet<WeakMember<FontCacheClient>>));
g_invalidate_font_cache = true;
return clients;
}
void FontCache::AddClient(FontCacheClient* client) {
CHECK(client);
DCHECK(!FontCacheClients().Contains(client));
FontCacheClients().insert(client);
}
static unsigned short g_generation = 0;
unsigned short FontCache::Generation() {
return g_generation;
}
void FontCache::Invalidate() {
if (!g_invalidate_font_cache) {
DCHECK(!g_font_platform_data_cache);
return;
}
if (g_font_platform_data_cache) {
delete g_font_platform_data_cache;
g_font_platform_data_cache = new FontPlatformDataCache;
}
g_generation++;
HeapVector<Member<FontCacheClient>> clients;
CopyToVector(FontCacheClients(), clients);
for (const auto& client : clients)
client->FontCacheInvalidated();
Purge(kForcePurge);
}
void FontCache::CrashWithFontInfo(const FontDescription* font_description) {
FontCache* font_cache = FontCache::GetFontCache();
SkFontMgr* font_mgr = nullptr;
int num_families = std::numeric_limits<int>::min();
if (font_cache) {
font_mgr = font_cache->font_manager_.get();
if (font_mgr)
num_families = font_mgr->countFamilies();
}
FontDescription font_description_copy = *font_description;
debug::Alias(&font_description_copy);
debug::Alias(&font_cache);
debug::Alias(&font_mgr);
debug::Alias(&num_families);
CHECK(false);
}
void FontCache::DumpFontPlatformDataCache(
base::trace_event::ProcessMemoryDump* memory_dump) {
DCHECK(IsMainThread());
if (!g_font_platform_data_cache)
return;
base::trace_event::MemoryAllocatorDump* dump =
memory_dump->CreateAllocatorDump("font_caches/font_platform_data_cache");
size_t font_platform_data_objects_size =
g_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());
if (!g_fallback_list_shaper_cache) {
return;
}
base::trace_event::MemoryAllocatorDump* dump =
memory_dump->CreateAllocatorDump("font_caches/shape_caches");
size_t shape_result_cache_size = 0;
FallbackListShaperCache::iterator iter;
for (iter = g_fallback_list_shaper_cache->begin();
iter != g_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);
}
} // namespace blink