Skip to content
Permalink
Browse files
Design an intentional way to invalidate platform font caches
https://bugs.webkit.org/show_bug.cgi?id=241537

Reviewed by Cameron McCormack and Simon Fraser.

Our platform font invalidation code is fairly spotty - the choice of "what invalidates what" isn't
particularly deliberate. This patch reorganizes our platform font invalidation code to have this
design (where the arrows represent "A calls B" relationships):

FontCache::invalidateAllFontCaches()
              ||
              ||
              ||
              V
    FontCache::invalidate()
              ||           \\
              ||            \\
              ||              =====
              ||                   \\
              ||                    V
              ||                    SystemFontDatabase::invalidate()
              V                                   ||
FontCache::platformInvalidate()                   ||
                                                  ||
                                                  ||
                                                  V
                                SystemFontDatabase::platformInvalidate()

So now, there is one clear place that should be called whenever this stuff needs to be invalidated:
FontCache::invalidateAllFontCaches(). This greatly simplifies the calculus of "which invalidation function
do I need to run in which situations." Also, because these invalidation functions are only called during
situations like the memory pressure handler, or when system preferences change, performance isn't a concern
here, and it's way more important that the relevant invalidation actually happens than it is to
exactly minimize the set of specific things that need to be invalidated in every specific situation.

On Cocoa ports, SystemFontDatabase::platformInvalidate() is implemented by SystemFontDatabaseCoreText,
which already exists. Also, this patch updates the existing platform notification handlers to call
the top level entry point FontCache::invalidateAllFontCaches() as expected.

This is a defensive patch. There's no specific bug or change in behavior I'm aiming for; instead I'm just
trying to make it more likely that the right thing happens at the right time. I'm also trying to create
an infrastructure I can plug into when I implement https://bugs.webkit.org/show_bug.cgi?id=237817.

No tests because this is a defensive patch.

* Source/WebCore/page/Page.cpp:
(WebCore::Page::firstTimeInitialization):
* Source/WebCore/page/cocoa/MemoryReleaseCocoa.mm:
(WebCore::platformReleaseMemory):
* Source/WebCore/platform/graphics/FontCache.cpp:
(WebCore::FontCache::invalidate):
* Source/WebCore/platform/graphics/FontCache.h:
(WebCore::FontCache::generation const):
* Source/WebCore/platform/graphics/SystemFontDatabase.cpp:
(WebCore::SystemFontDatabase::invalidate):
(WebCore::SystemFontDatabase::clear): Deleted.
* Source/WebCore/platform/graphics/SystemFontDatabase.h:
* Source/WebCore/platform/graphics/cocoa/FontCacheCoreText.cpp:
(WebCore::fontCacheRegisteredFontsChangedNotificationCallback):
(WebCore::FontCache::platformInvalidate):
(WebCore::invalidateFontCache): Deleted.
* Source/WebCore/platform/graphics/cocoa/SystemFontDatabaseCoreText.cpp:
(WebCore::SystemFontDatabase::platformInvalidate):
(WebCore::SystemFontDatabaseCoreText::clear):
* Source/WebCore/platform/graphics/cocoa/SystemFontDatabaseCoreText.h:
* Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp:
(WebCore::FontCache::platformInvalidate):
* Source/WebCore/platform/graphics/gtk/SystemFontDatabaseGTK.cpp:
(WebCore::SystemFontDatabase::platformInvalidate):
* Source/WebCore/platform/graphics/playstation/SystemFontDatabasePlayStation.cpp:
(WebCore::SystemFontDatabase::platformInvalidate):
* Source/WebCore/platform/graphics/win/FontCacheWin.cpp:
(WebCore::FontCache::platformInvalidate):
* Source/WebCore/platform/graphics/win/SystemFontDatabaseWin.cpp:
(WebCore::SystemFontDatabase::platformInvalidate):
* Source/WebCore/platform/graphics/wpe/SystemFontDatabaseWPE.cpp:
(WebCore::SystemFontDatabase::platformInvalidate):
* Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::contentSizeCategoryDidChange):
* Source/WebKit/WebProcess/WebProcess.cpp:
(WebKit::WebProcess::terminate):
* Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm:
(WebKit::WebProcess::accessibilityPreferencesDidChange):

Canonical link: https://commits.webkit.org/251845@main
  • Loading branch information
litherum committed Jun 25, 2022
1 parent 9e683e6 commit 78afcc43874cb89741751616c94819a3e5e4cc68
Showing 18 changed files with 82 additions and 39 deletions.
@@ -437,9 +437,7 @@ void Page::firstTimeInitialization()
platformStrategies()->loaderStrategy()->addOnlineStateChangeListener(&networkStateChanged);

FontCache::registerFontCacheInvalidationCallback([] {
forEachPage([](auto& page) {
page.setNeedsRecalcStyleInAllFrames();
});
updateStyleForAllPagesAfterGlobalChangeInEnvironment();
});
}

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2015 Apple Inc. All rights reserved.
* Copyright (C) 2011-2022 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,14 +26,14 @@
#import "config.h"
#import "MemoryRelease.h"

#import "FontCache.h"
#import "FontFamilySpecificationCoreText.h"
#import "GCController.h"
#import "HTMLNameCache.h"
#import "IOSurfacePool.h"
#import "LayerPool.h"
#import "LocaleCocoa.h"
#import "SubimageCacheWithTimer.h"
#import "SystemFontDatabaseCoreText.h"
#import <notify.h>
#import <pal/spi/ios/GraphicsServicesSPI.h>

@@ -47,7 +47,7 @@

void platformReleaseMemory(Critical)
{
SystemFontDatabaseCoreText::singleton().clear();
FontCache::invalidateAllFontCaches(FontCache::ShouldRunInvalidationCallback::No);
clearFontFamilySpecificationCoreTextCache();

#if PLATFORM(IOS_FAMILY) && !PLATFORM(IOS_FAMILY_SIMULATOR) && !PLATFORM(MACCATALYST)
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2006-2021 Apple Inc. All rights reserved.
* Copyright (C) 2006-2022 Apple Inc. All rights reserved.
* Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
*
* Redistribution and use in source and binary forms, with or without
@@ -35,6 +35,7 @@
#include "FontPlatformData.h"
#include "FontSelector.h"
#include "Logging.h"
#include "SystemFontDatabase.h"
#include "ThreadGlobalData.h"
#include "WebKitFontFamilyNames.h"
#include "WorkerOrWorkletThread.h"
@@ -481,6 +482,10 @@ void FontCache::invalidate()
#endif
invalidateFontCascadeCache();

SystemFontDatabase::singleton().invalidate();

platformInvalidate();

++m_generation;

for (auto& client : copyToVectorOf<RefPtr<FontSelector>>(m_clients))
@@ -500,14 +505,14 @@ void FontCache::registerFontCacheInvalidationCallback(Function<void()>&& callbac
fontCacheInvalidationCallback() = WTFMove(callback);
}

void FontCache::invalidateAllFontCaches()
void FontCache::invalidateAllFontCaches(ShouldRunInvalidationCallback shouldRunInvalidationCallback)
{
ASSERT(isMainThread());

// FIXME: Invalidate FontCaches in workers too.
FontCache::forCurrentThread().invalidate();

if (fontCacheInvalidationCallback())
if (shouldRunInvalidationCallback == ShouldRunInvalidationCallback::Yes && fontCacheInvalidationCallback())
fontCacheInvalidationCallback()();
}

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2006-2021 Apple Inc. All rights reserved.
* Copyright (C) 2006-2022 Apple Inc. All rights reserved.
* Copyright (C) 2007-2008 Torch Mobile, Inc.
*
* Redistribution and use in source and binary forms, with or without
@@ -327,9 +327,19 @@ class FontCache {
void removeClient(FontSelector&);

unsigned short generation() const { return m_generation; }
WEBCORE_EXPORT void invalidate();
static void registerFontCacheInvalidationCallback(Function<void()>&&);
WEBCORE_EXPORT static void invalidateAllFontCaches();

// The invalidation callback runs a style recalc on the page.
// If we're invalidating because of memory pressure, we shouldn't run a style recalc.
// A style recalc would just allocate a bunch of the memory that we're trying to release.
// On the other hand, if we're invalidating because the set of installed fonts changed,
// or if some accessibility text settings were altered, we should run a style recalc
// so the user can immediately see the effect of the new environment.
enum class ShouldRunInvalidationCallback : bool {
No,
Yes
};
WEBCORE_EXPORT static void invalidateAllFontCaches(ShouldRunInvalidationCallback = ShouldRunInvalidationCallback::Yes);

WEBCORE_EXPORT size_t fontCount();
WEBCORE_EXPORT size_t inactiveFontCount();
@@ -369,6 +379,9 @@ class FontCache {
static void prewarmGlobally();

private:
void invalidate();
void platformInvalidate();

WEBCORE_EXPORT void purgeInactiveFontDataIfNeeded();
void pruneUnreferencedEntriesFromFontCascadeCache();
void pruneSystemFallbackFonts();
@@ -53,10 +53,11 @@ FontSelectionValue SystemFontDatabase::systemFontShorthandWeight(FontShorthand f
return systemFontShorthandInfo(fontShorthand).weight;
}

void SystemFontDatabase::clear()
void SystemFontDatabase::invalidate()
{
for (auto& item : m_systemFontShorthandCache)
item.reset();
platformInvalidate();
}

} // namespace WebCore
@@ -74,12 +74,15 @@ class SystemFontDatabase {
float systemFontShorthandSize(FontShorthand);
FontSelectionValue systemFontShorthandWeight(FontShorthand);

WEBCORE_EXPORT void clear();

protected:
SystemFontDatabase();

private:
friend class FontCache;

void invalidate();
void platformInvalidate();

struct SystemFontShorthandInfo {
AtomString family;
float size;
@@ -796,13 +796,13 @@ static float stretchFromCoreTextTraits(CFDictionaryRef traits)
}
#endif

static void invalidateFontCache();

static void fontCacheRegisteredFontsChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef, const void *, CFDictionaryRef)
{
ASSERT_UNUSED(observer, isMainThread() && observer == &FontCache::forCurrentThread());

invalidateFontCache();
ensureOnMainThread([] {
FontCache::invalidateAllFontCaches();
});
}

void FontCache::platformInit()
@@ -1287,20 +1287,15 @@ static FontLookup platformFontLookupWithFamily(const AtomString& family, FontSel
return { nullptr };
}

static void invalidateFontCache()
void FontCache::platformInvalidate()
{
ensureOnMainThread([] {
// FIXME: Workers need to access SystemFontDatabaseCoreText.
SystemFontDatabaseCoreText::singleton().clear();
// FIXME: Workers need to access FontFamilySpecificationCoreTextCache.
clearFontFamilySpecificationCoreTextCache();
// FIXME: Workers need to access SystemFontDatabaseCoreText.
// FIXME: Workers need to access FontFamilySpecificationCoreTextCache.
clearFontFamilySpecificationCoreTextCache();

// FIXME: Workers need to access FontDatabase.
FontDatabase::singletonAllowingUserInstalledFonts().clear();
FontDatabase::singletonDisallowingUserInstalledFonts().clear();

FontCache::invalidateAllFontCaches();
});
// FIXME: Workers need to access FontDatabase.
FontDatabase::singletonAllowingUserInstalledFonts().clear();
FontDatabase::singletonDisallowingUserInstalledFonts().clear();
}

static RetainPtr<CTFontRef> fontWithFamilySpecialCase(const AtomString& family, const FontDescription& fontDescription, float size, AllowUserInstalledFonts allowUserInstalledFonts)
@@ -130,15 +130,20 @@ Vector<RetainPtr<CTFontDescriptorRef>> SystemFontDatabaseCoreText::cascadeList(c
}).iterator->value;
}

void SystemFontDatabase::platformInvalidate()
{
SystemFontDatabaseCoreText::singleton().clear();
}

void SystemFontDatabaseCoreText::clear()
{
// Don't call this directly. Instead, you should be calling FontCache::invalidateAllFontCaches().
m_systemFontCache.clear();
m_serifFamilies.clear();
m_sansSeriferifFamilies.clear();
m_cursiveFamilies.clear();
m_fantasyFamilies.clear();
m_monospaceFamilies.clear();
SystemFontDatabase::clear();
}

RetainPtr<CTFontRef> SystemFontDatabaseCoreText::createFontByApplyingWeightWidthItalicsAndFallbackBehavior(CTFontRef font, CGFloat weight, CGFloat width, bool italic, float size, AllowUserInstalledFonts allowUserInstalledFonts, CFStringRef design)
@@ -101,6 +101,7 @@ class SystemFontDatabaseCoreText : public SystemFontDatabase {
float systemFontShorthandSize(FontShorthand);
FontSelectionValue systemFontShorthandWeight(FontShorthand);

protected:
void clear();

private:
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2008 Alp Toker <alp@atoker.com>
* Copyright (C) 2010 Igalia S.L.
* Copyright (C) 2022 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -681,4 +682,8 @@ String buildVariationSettings(FT_Face face, const FontDescription& fontDescripti
}
#endif // ENABLE(VARIATION_FONTS)

void FontCache::platformInvalidate()
{
}

}
@@ -62,4 +62,8 @@ auto SystemFontDatabase::platformSystemFontShorthandInfo(FontShorthand) -> Syste
return result;
}

void SystemFontDatabase::platformInvalidate()
{
}

} // namespace WebCore
@@ -45,4 +45,8 @@ auto SystemFontDatabase::platformSystemFontShorthandInfo(FontShorthand fontShort
return { WebKitFontFamilyNames::standardFamily, 16, normalWeightValue() };
}

void SystemFontDatabase::platformInvalidate()
{
}

} // namespace WebCore
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2006-2008, 2013-2014 Apple Inc. All rights reserved.
* Copyright (C) 2006-2022 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -714,4 +714,8 @@ std::optional<ASCIILiteral> FontCache::platformAlternateFamilyName(const String&
return std::nullopt;
}

void FontCache::platformInvalidate()
{
}

}
@@ -85,4 +85,8 @@ auto SystemFontDatabase::platformSystemFontShorthandInfo(FontShorthand fontShort
return { logFont.lfFaceName, size, weight };
}

void SystemFontDatabase::platformInvalidate()
{
}

} // namespace WebCore
@@ -45,4 +45,8 @@ auto SystemFontDatabase::platformSystemFontShorthandInfo(FontShorthand) -> Syste
return { WebKitFontFamilyNames::standardFamily, 16, normalWeightValue() };
}

void SystemFontDatabase::platformInvalidate()
{
}

} // namespace WebCore
@@ -87,6 +87,7 @@
#import <WebCore/File.h>
#import <WebCore/FloatQuad.h>
#import <WebCore/FocusController.h>
#import <WebCore/FontCache.h>
#import <WebCore/FontCacheCoreText.h>
#import <WebCore/Frame.h>
#import <WebCore/FrameLoaderClient.h>
@@ -142,7 +143,6 @@
#import <WebCore/ShadowRoot.h>
#import <WebCore/SharedBuffer.h>
#import <WebCore/StyleProperties.h>
#import <WebCore/SystemFontDatabase.h>
#import <WebCore/TextIndicator.h>
#import <WebCore/TextIterator.h>
#import <WebCore/TextPlaceholderElement.h>
@@ -4395,8 +4395,7 @@ static bool selectionIsInsideFixedPositionContainer(Frame& frame)
void WebPage::contentSizeCategoryDidChange(const String& contentSizeCategory)
{
setContentSizeCategory(contentSizeCategory);
SystemFontDatabase::singleton().clear();
Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
FontCache::invalidateAllFontCaches();
}

String WebPage::platformUserAgent(const URL&) const
@@ -893,7 +893,7 @@ void WebProcess::terminate()
#ifndef NDEBUG
// These are done in an attempt to reduce LEAK output.
GCController::singleton().garbageCollectNow();
FontCache::forCurrentThread().invalidate();
FontCache::invalidateAllFontCaches();
MemoryCache::singleton().setDisabled(true);
#endif

@@ -83,7 +83,6 @@
#import <WebCore/RuntimeEnabledFeatures.h>
#import <WebCore/SWContextManager.h>
#import <WebCore/SystemBattery.h>
#import <WebCore/SystemFontDatabase.h>
#import <WebCore/SystemSoundManager.h>
#import <WebCore/UTIUtilities.h>
#import <WebCore/WebMAudioUtilitiesCocoa.h>
@@ -1019,8 +1018,7 @@ static float currentBacklightLevel()
auto invertColorsEnabled = preferences.invertColorsEnabled;
if (_AXSInvertColorsEnabledApp(appID) != invertColorsEnabled)
_AXSInvertColorsSetEnabledApp(invertColorsEnabled, appID);
SystemFontDatabase::singleton().clear();
Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
FontCache::invalidateAllFontCaches();
#endif
}

0 comments on commit 78afcc4

Please sign in to comment.