Skip to content

Commit

Permalink
Decouple font creation from font loading
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=153414

Reviewed by Darin Adler.

Previously, CSSFontFaceSource never triggered a font download until that font was actually used. This means
that the function which triggers the download also has the goal of returning a font to use. However,
the CSS Font Loading JavaScript API requires being able to trigger a font download without this extra font
creation overhead.

In addition, this patch adds an explicit (and enforced) state transition diagram. The diagram looks like
this:
                    => Success
                  //
Pending => Loading
                  \\
                    => Failure

Therefore, the API for CSSFontFaceSource has changed to expose the concept of these new states. This means
that its user (CSSSegmentedFontFaceSource) has been updated to handle each possible state that its constituent
CSSFontFaceSources may be in.

No new tests because there is no behavior change.

* css/CSSFontFace.cpp:
(WebCore::CSSFontFace::allSourcesFailed): Renamed to make the name clearer.
(WebCore::CSSFontFace::addedToSegmentedFontFace): Use references instead of pointers.
(WebCore::CSSFontFace::removedFromSegmentedFontFace): Ditto.
(WebCore::CSSFontFace::adoptSource): Renamed to make the name clearer.
(WebCore::CSSFontFace::fontLoaded): Use references instead of pointers. Also, remove old dead code.
(WebCore::CSSFontFace::font): Adapt to the new API of CSSFontFaceSource.
(WebCore::CSSFontFace::isValid): Deleted.
(WebCore::CSSFontFace::addSource): Deleted.
(WebCore::CSSFontFace::notifyFontLoader): Deleted. Old dead code.
(WebCore::CSSFontFace::notifyLoadingDone): Deleted. Old dead code.
* css/CSSFontFace.h:
(WebCore::CSSFontFace::create): Remove old dead code.
(WebCore::CSSFontFace::CSSFontFace): Use references instead of pointers.
(WebCore::CSSFontFace::loadState): Deleted. Remove old dead code.
* css/CSSFontFaceSource.cpp:
(WebCore::CSSFontFaceSource::setStatus): Enforce state transitions.
(WebCore::CSSFontFaceSource::CSSFontFaceSource): Explicitly handle new state transitions.
(WebCore::CSSFontFaceSource::fontLoaded): Update for new states.
(WebCore::CSSFontFaceSource::load): Pulled out code from font().
(WebCore::CSSFontFaceSource::font): Moved code into load().
(WebCore::CSSFontFaceSource::isValid): Deleted.
(WebCore::CSSFontFaceSource::isDecodeError): Deleted.
(WebCore::CSSFontFaceSource::ensureFontData): Deleted.
* css/CSSFontFaceSource.h: Much cleaner API.
* css/CSSFontSelector.cpp:
(WebCore::createFontFace): Migrate to references instead of pointers. This requires a little
reorganization.
(WebCore::registerLocalFontFacesForFamily): Update to new CSSFontFaceSource API.
(WebCore::CSSFontSelector::addFontFaceRule): Ditto.
(WebCore::CSSFontSelector::getFontFace): Ditto.
* css/CSSSegmentedFontFace.cpp:
(WebCore::CSSSegmentedFontFace::CSSSegmentedFontFace): Migrate to references instead of pointers.
(WebCore::CSSSegmentedFontFace::~CSSSegmentedFontFace): Ditto.
(WebCore::CSSSegmentedFontFace::fontLoaded): Remove old dead code.
(WebCore::CSSSegmentedFontFace::appendFontFace): Cleanup.
(WebCore::CSSSegmentedFontFace::fontRanges): Adopt to new API.
(WebCore::CSSSegmentedFontFace::pruneTable): Deleted.
(WebCore::CSSSegmentedFontFace::isLoading): Deleted. Old dead code.
(WebCore::CSSSegmentedFontFace::checkFont): Deleted. Ditto.
(WebCore::CSSSegmentedFontFace::loadFont): Deleted. Ditto.
* css/CSSSegmentedFontFace.h:
(WebCore::CSSSegmentedFontFace::create): Migrate to references instead of pointers.
(WebCore::CSSSegmentedFontFace::fontSelector): Ditto.
(WebCore::CSSSegmentedFontFace::LoadFontCallback::~LoadFontCallback): Deleted.
* loader/cache/CachedFont.cpp:
(WebCore::CachedFont::didAddClient): Migrate to references instead of pointers.
(WebCore::CachedFont::checkNotify): Ditto.
* loader/cache/CachedFontClient.h:
(WebCore::CachedFontClient::fontLoaded): Ditto.

Canonical link: https://commits.webkit.org/172140@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@196322 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
litherum committed Feb 9, 2016
1 parent e2cb24d commit 2669277
Show file tree
Hide file tree
Showing 10 changed files with 265 additions and 334 deletions.
77 changes: 77 additions & 0 deletions Source/WebCore/ChangeLog
@@ -1,3 +1,80 @@
2016-02-09 Myles C. Maxfield <mmaxfield@apple.com>

Decouple font creation from font loading
https://bugs.webkit.org/show_bug.cgi?id=153414

Reviewed by Darin Adler.

Previously, CSSFontFaceSource never triggered a font download until that font was actually used. This means
that the function which triggers the download also has the goal of returning a font to use. However,
the CSS Font Loading JavaScript API requires being able to trigger a font download without this extra font
creation overhead.

In addition, this patch adds an explicit (and enforced) state transition diagram. The diagram looks like
this:
=> Success
//
Pending => Loading
\\
=> Failure

Therefore, the API for CSSFontFaceSource has changed to expose the concept of these new states. This means
that its user (CSSSegmentedFontFaceSource) has been updated to handle each possible state that its constituent
CSSFontFaceSources may be in.

No new tests because there is no behavior change.

* css/CSSFontFace.cpp:
(WebCore::CSSFontFace::allSourcesFailed): Renamed to make the name clearer.
(WebCore::CSSFontFace::addedToSegmentedFontFace): Use references instead of pointers.
(WebCore::CSSFontFace::removedFromSegmentedFontFace): Ditto.
(WebCore::CSSFontFace::adoptSource): Renamed to make the name clearer.
(WebCore::CSSFontFace::fontLoaded): Use references instead of pointers. Also, remove old dead code.
(WebCore::CSSFontFace::font): Adapt to the new API of CSSFontFaceSource.
(WebCore::CSSFontFace::isValid): Deleted.
(WebCore::CSSFontFace::addSource): Deleted.
(WebCore::CSSFontFace::notifyFontLoader): Deleted. Old dead code.
(WebCore::CSSFontFace::notifyLoadingDone): Deleted. Old dead code.
* css/CSSFontFace.h:
(WebCore::CSSFontFace::create): Remove old dead code.
(WebCore::CSSFontFace::CSSFontFace): Use references instead of pointers.
(WebCore::CSSFontFace::loadState): Deleted. Remove old dead code.
* css/CSSFontFaceSource.cpp:
(WebCore::CSSFontFaceSource::setStatus): Enforce state transitions.
(WebCore::CSSFontFaceSource::CSSFontFaceSource): Explicitly handle new state transitions.
(WebCore::CSSFontFaceSource::fontLoaded): Update for new states.
(WebCore::CSSFontFaceSource::load): Pulled out code from font().
(WebCore::CSSFontFaceSource::font): Moved code into load().
(WebCore::CSSFontFaceSource::isValid): Deleted.
(WebCore::CSSFontFaceSource::isDecodeError): Deleted.
(WebCore::CSSFontFaceSource::ensureFontData): Deleted.
* css/CSSFontFaceSource.h: Much cleaner API.
* css/CSSFontSelector.cpp:
(WebCore::createFontFace): Migrate to references instead of pointers. This requires a little
reorganization.
(WebCore::registerLocalFontFacesForFamily): Update to new CSSFontFaceSource API.
(WebCore::CSSFontSelector::addFontFaceRule): Ditto.
(WebCore::CSSFontSelector::getFontFace): Ditto.
* css/CSSSegmentedFontFace.cpp:
(WebCore::CSSSegmentedFontFace::CSSSegmentedFontFace): Migrate to references instead of pointers.
(WebCore::CSSSegmentedFontFace::~CSSSegmentedFontFace): Ditto.
(WebCore::CSSSegmentedFontFace::fontLoaded): Remove old dead code.
(WebCore::CSSSegmentedFontFace::appendFontFace): Cleanup.
(WebCore::CSSSegmentedFontFace::fontRanges): Adopt to new API.
(WebCore::CSSSegmentedFontFace::pruneTable): Deleted.
(WebCore::CSSSegmentedFontFace::isLoading): Deleted. Old dead code.
(WebCore::CSSSegmentedFontFace::checkFont): Deleted. Ditto.
(WebCore::CSSSegmentedFontFace::loadFont): Deleted. Ditto.
* css/CSSSegmentedFontFace.h:
(WebCore::CSSSegmentedFontFace::create): Migrate to references instead of pointers.
(WebCore::CSSSegmentedFontFace::fontSelector): Ditto.
(WebCore::CSSSegmentedFontFace::LoadFontCallback::~LoadFontCallback): Deleted.
* loader/cache/CachedFont.cpp:
(WebCore::CachedFont::didAddClient): Migrate to references instead of pointers.
(WebCore::CachedFont::checkNotify): Ditto.
* loader/cache/CachedFontClient.h:
(WebCore::CachedFontClient::fontLoaded): Ditto.

2016-02-09 Brady Eidson <beidson@apple.com>

Modern IDB: IDBOpenDBRequests leak.
Expand Down
127 changes: 33 additions & 94 deletions Source/WebCore/css/CSSFontFace.cpp
Expand Up @@ -37,135 +37,74 @@

namespace WebCore {

bool CSSFontFace::isValid() const
bool CSSFontFace::allSourcesFailed() const
{
size_t size = m_sources.size();
for (size_t i = 0; i < size; i++) {
if (m_sources[i]->isValid())
return true;
for (auto& source : m_sources) {
if (source->status() != CSSFontFaceSource::Status::Failure)
return false;
}
return false;
return true;
}

void CSSFontFace::addedToSegmentedFontFace(CSSSegmentedFontFace* segmentedFontFace)
void CSSFontFace::addedToSegmentedFontFace(CSSSegmentedFontFace& segmentedFontFace)
{
m_segmentedFontFaces.add(segmentedFontFace);
m_segmentedFontFaces.add(&segmentedFontFace);
}

void CSSFontFace::removedFromSegmentedFontFace(CSSSegmentedFontFace* segmentedFontFace)
void CSSFontFace::removedFromSegmentedFontFace(CSSSegmentedFontFace& segmentedFontFace)
{
m_segmentedFontFaces.remove(segmentedFontFace);
m_segmentedFontFaces.remove(&segmentedFontFace);
}

void CSSFontFace::addSource(std::unique_ptr<CSSFontFaceSource> source)
void CSSFontFace::adoptSource(std::unique_ptr<CSSFontFaceSource>&& source)
{
source->setFontFace(this);
m_sources.append(WTFMove(source));
}

void CSSFontFace::fontLoaded(CSSFontFaceSource* source)
void CSSFontFace::fontLoaded(CSSFontFaceSource&)
{
if (source != m_activeSource)
return;

// FIXME: Can we assert that m_segmentedFontFaces is not empty? That may
// require stopping in-progress font loading when the last
// CSSSegmentedFontFace is removed.
if (m_segmentedFontFaces.isEmpty())
return;

// Use one of the CSSSegmentedFontFaces' font selector. They all have
// the same font selector, so it's wasteful to store it in the CSSFontFace.
CSSFontSelector* fontSelector = (*m_segmentedFontFaces.begin())->fontSelector();
fontSelector->fontLoaded();

#if ENABLE(FONT_LOAD_EVENTS)
if (RuntimeEnabledFeatures::sharedFeatures().fontLoadEventsEnabled() && m_loadState == Loading) {
if (source->ensureFontData())
notifyFontLoader(Loaded);
else if (!isValid())
notifyFontLoader(Error);
}
#endif
(*m_segmentedFontFaces.begin())->fontSelector().fontLoaded();

for (auto* face : m_segmentedFontFaces)
face->fontLoaded(this);

#if ENABLE(FONT_LOAD_EVENTS)
if (RuntimeEnabledFeatures::sharedFeatures().fontLoadEventsEnabled())
notifyLoadingDone();
#endif
face->fontLoaded(*this);
}

RefPtr<Font> CSSFontFace::font(const FontDescription& fontDescription, bool syntheticBold, bool syntheticItalic)
{
m_activeSource = 0;
if (!isValid())
return 0;
if (allSourcesFailed())
return nullptr;

ASSERT(!m_segmentedFontFaces.isEmpty());
CSSFontSelector* fontSelector = (*m_segmentedFontFaces.begin())->fontSelector();

#if ENABLE(FONT_LOAD_EVENTS)
if (RuntimeEnabledFeatures::sharedFeatures().fontLoadEventsEnabled() && m_loadState == NotLoaded)
notifyFontLoader(Loading);
#endif

size_t size = m_sources.size();
for (size_t i = 0; i < size; ++i) {
if (RefPtr<Font> result = m_sources[i]->font(fontDescription, syntheticBold, syntheticItalic, fontSelector, m_featureSettings, m_variantSettings)) {
m_activeSource = m_sources[i].get();
#if ENABLE(FONT_LOAD_EVENTS)
if (RuntimeEnabledFeatures::sharedFeatures().fontLoadEventsEnabled() && m_loadState == Loading && m_sources[i]->isLoaded()) {
notifyFontLoader(Loaded);
notifyLoadingDone();
}
#endif
return result.release();
CSSFontSelector& fontSelector = (*m_segmentedFontFaces.begin())->fontSelector();

for (auto& source : m_sources) {
if (source->status() == CSSFontFaceSource::Status::Pending)
source->load(fontSelector);

switch (source->status()) {
case CSSFontFaceSource::Status::Pending:
ASSERT_NOT_REACHED();
break;
case CSSFontFaceSource::Status::Loading:
return Font::create(FontCache::singleton().lastResortFallbackFont(fontDescription)->platformData(), true, true);
case CSSFontFaceSource::Status::Success:
if (RefPtr<Font> result = source->font(fontDescription, syntheticBold, syntheticItalic, m_featureSettings, m_variantSettings))
return WTFMove(result);
break;
case CSSFontFaceSource::Status::Failure:
break;
}
}

#if ENABLE(FONT_LOAD_EVENTS)
if (RuntimeEnabledFeatures::sharedFeatures().fontLoadEventsEnabled() && m_loadState == Loading) {
notifyFontLoader(Error);
notifyLoadingDone();
}
#endif
return nullptr;
}

#if ENABLE(FONT_LOAD_EVENTS)
void CSSFontFace::notifyFontLoader(LoadState newState)
{
m_loadState = newState;

Document* document = (*m_segmentedFontFaces.begin())->fontSelector()->document();
if (!document)
return;

switch (newState) {
case Loading:
document->fonts()->beginFontLoading(m_rule.get());
break;
case Loaded:
document->fonts()->fontLoaded(m_rule.get());
break;
case Error:
document->fonts()->loadError(m_rule.get(), m_activeSource);
break;
default:
break;
}
}

void CSSFontFace::notifyLoadingDone()
{
Document* document = (*m_segmentedFontFaces.begin())->fontSelector()->document();
if (document)
document->fonts()->loadingDone();
}
#endif

#if ENABLE(SVG_FONTS)
bool CSSFontFace::hasSVGFontFaceSource() const
{
Expand Down
32 changes: 7 additions & 25 deletions Source/WebCore/css/CSSFontFace.h
Expand Up @@ -45,7 +45,7 @@ class Font;

class CSSFontFace : public RefCounted<CSSFontFace> {
public:
static Ref<CSSFontFace> create(FontTraitsMask traitsMask, RefPtr<CSSFontFaceRule>&& rule, bool isLocalFallback = false) { return adoptRef(*new CSSFontFace(traitsMask, WTFMove(rule), isLocalFallback)); }
static Ref<CSSFontFace> create(FontTraitsMask traitsMask, bool isLocalFallback = false) { return adoptRef(*new CSSFontFace(traitsMask, isLocalFallback)); }

FontTraitsMask traitsMask() const { return m_traitsMask; }

Expand All @@ -72,16 +72,16 @@ class CSSFontFace : public RefCounted<CSSFontFace> {
void setVariantEastAsianWidth(FontVariantEastAsianWidth width) { m_variantSettings.eastAsianWidth = width; }
void setVariantEastAsianRuby(FontVariantEastAsianRuby ruby) { m_variantSettings.eastAsianRuby = ruby; }

void addedToSegmentedFontFace(CSSSegmentedFontFace*);
void removedFromSegmentedFontFace(CSSSegmentedFontFace*);
void addedToSegmentedFontFace(CSSSegmentedFontFace&);
void removedFromSegmentedFontFace(CSSSegmentedFontFace&);

bool isValid() const;
bool allSourcesFailed() const;

bool isLocalFallback() const { return m_isLocalFallback; }

void addSource(std::unique_ptr<CSSFontFaceSource>);
void adoptSource(std::unique_ptr<CSSFontFaceSource>&&);

void fontLoaded(CSSFontFaceSource*);
void fontLoaded(CSSFontFaceSource&);

RefPtr<Font> font(const FontDescription&, bool syntheticBold, bool syntheticItalic);

Expand All @@ -104,22 +104,11 @@ class CSSFontFace : public RefCounted<CSSFontFace> {
bool hasSVGFontFaceSource() const;
#endif

#if ENABLE(FONT_LOAD_EVENTS)
enum LoadState { NotLoaded, Loading, Loaded, Error };
LoadState loadState() const { return m_loadState; }
#endif

private:
CSSFontFace(FontTraitsMask traitsMask, RefPtr<CSSFontFaceRule>&& rule, bool isLocalFallback)
CSSFontFace(FontTraitsMask traitsMask, bool isLocalFallback)
: m_traitsMask(traitsMask)
, m_activeSource(0)
, m_isLocalFallback(isLocalFallback)
#if ENABLE(FONT_LOAD_EVENTS)
, m_loadState(isLocalFallback ? Loaded : NotLoaded)
, m_rule(rule)
#endif
{
UNUSED_PARAM(rule);
}

FontTraitsMask m_traitsMask;
Expand All @@ -128,14 +117,7 @@ class CSSFontFace : public RefCounted<CSSFontFace> {
FontFeatureSettings m_featureSettings;
FontVariantSettings m_variantSettings;
Vector<std::unique_ptr<CSSFontFaceSource>> m_sources;
CSSFontFaceSource* m_activeSource;
bool m_isLocalFallback;
#if ENABLE(FONT_LOAD_EVENTS)
LoadState m_loadState;
RefPtr<CSSFontFaceRule> m_rule;
void notifyFontLoader(LoadState);
void notifyLoadingDone();
#endif
};

}
Expand Down

0 comments on commit 2669277

Please sign in to comment.