1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "config.h"
6#include "core/css/RemoteFontFaceSource.h"
7
8#include "core/css/CSSCustomFontData.h"
9#include "core/css/CSSFontFace.h"
10#include "core/css/FontLoader.h"
11#include "platform/fonts/FontCache.h"
12#include "platform/fonts/FontDescription.h"
13#include "platform/fonts/SimpleFontData.h"
14#include "public/platform/Platform.h"
15#include "wtf/CurrentTime.h"
16
17namespace blink {
18
19RemoteFontFaceSource::RemoteFontFaceSource(FontResource* font, PassRefPtrWillBeRawPtr<FontLoader> fontLoader)
20    : m_font(font)
21    , m_fontLoader(fontLoader)
22{
23    m_font->addClient(this);
24}
25
26RemoteFontFaceSource::~RemoteFontFaceSource()
27{
28    m_font->removeClient(this);
29    pruneTable();
30}
31
32void RemoteFontFaceSource::pruneTable()
33{
34    if (m_fontDataTable.isEmpty())
35        return;
36
37    for (FontDataTable::iterator it = m_fontDataTable.begin(); it != m_fontDataTable.end(); ++it) {
38        SimpleFontData* fontData = it->value.get();
39        if (fontData && fontData->customFontData())
40            fontData->customFontData()->clearFontFaceSource();
41    }
42    m_fontDataTable.clear();
43}
44
45bool RemoteFontFaceSource::isLoading() const
46{
47    return !m_font->stillNeedsLoad() && !m_font->isLoaded();
48}
49
50bool RemoteFontFaceSource::isLoaded() const
51{
52    return m_font->isLoaded();
53}
54
55bool RemoteFontFaceSource::isValid() const
56{
57    return !m_font->errorOccurred();
58}
59
60void RemoteFontFaceSource::didStartFontLoad(FontResource*)
61{
62    // We may send duplicated reports when multiple CSSFontFaceSource are
63    // registered at this FontResource. Associating the same URL to different
64    // font-family causes the case, but we treat them as indivisual resources.
65    m_histograms.loadStarted();
66}
67
68void RemoteFontFaceSource::fontLoaded(FontResource*)
69{
70    m_histograms.recordRemoteFont(m_font.get());
71
72    pruneTable();
73    if (m_face) {
74        m_fontLoader->fontFaceInvalidated();
75        m_face->fontLoaded(this);
76    }
77}
78
79void RemoteFontFaceSource::fontLoadWaitLimitExceeded(FontResource*)
80{
81    pruneTable();
82    if (m_face) {
83        m_fontLoader->fontFaceInvalidated();
84        m_face->fontLoadWaitLimitExceeded(this);
85    }
86
87    m_histograms.recordFallbackTime(m_font.get());
88}
89
90PassRefPtr<SimpleFontData> RemoteFontFaceSource::createFontData(const FontDescription& fontDescription)
91{
92    if (!isLoaded())
93        return createLoadingFallbackFontData(fontDescription);
94
95    // Create new FontPlatformData from our CGFontRef, point size and ATSFontRef.
96    if (!m_font->ensureCustomFontData())
97        return nullptr;
98
99    m_histograms.recordFallbackTime(m_font.get());
100
101    return SimpleFontData::create(
102        m_font->platformDataFromCustomData(fontDescription.effectiveFontSize(),
103            fontDescription.isSyntheticBold(), fontDescription.isSyntheticItalic(),
104            fontDescription.orientation(), fontDescription.widthVariant()), CustomFontData::create());
105}
106
107PassRefPtr<SimpleFontData> RemoteFontFaceSource::createLoadingFallbackFontData(const FontDescription& fontDescription)
108{
109    // This temporary font is not retained and should not be returned.
110    FontCachePurgePreventer fontCachePurgePreventer;
111    SimpleFontData* temporaryFont = FontCache::fontCache()->getNonRetainedLastResortFallbackFont(fontDescription);
112    if (!temporaryFont) {
113        ASSERT_NOT_REACHED();
114        return nullptr;
115    }
116    RefPtr<CSSCustomFontData> cssFontData = CSSCustomFontData::create(this, m_font->exceedsFontLoadWaitLimit() ? CSSCustomFontData::VisibleFallback : CSSCustomFontData::InvisibleFallback);
117    return SimpleFontData::create(temporaryFont->platformData(), cssFontData);
118}
119
120void RemoteFontFaceSource::beginLoadIfNeeded()
121{
122    if (m_font->stillNeedsLoad())
123        m_fontLoader->addFontToBeginLoading(m_font.get());
124
125    if (m_face)
126        m_face->didBeginLoad();
127}
128
129bool RemoteFontFaceSource::ensureFontData()
130{
131    return m_font->ensureCustomFontData();
132}
133
134void RemoteFontFaceSource::trace(Visitor* visitor)
135{
136    visitor->trace(m_fontLoader);
137    CSSFontFaceSource::trace(visitor);
138}
139
140void RemoteFontFaceSource::FontLoadHistograms::loadStarted()
141{
142    if (!m_loadStartTime)
143        m_loadStartTime = currentTimeMS();
144}
145
146void RemoteFontFaceSource::FontLoadHistograms::fallbackFontPainted()
147{
148    if (!m_fallbackPaintTime)
149        m_fallbackPaintTime = currentTimeMS();
150}
151
152void RemoteFontFaceSource::FontLoadHistograms::recordFallbackTime(const FontResource* font)
153{
154    if (m_fallbackPaintTime <= 0)
155        return;
156    int duration = static_cast<int>(currentTimeMS() - m_fallbackPaintTime);
157    blink::Platform::current()->histogramCustomCounts("WebFont.BlankTextShownTime", duration, 0, 10000, 50);
158    m_fallbackPaintTime = -1;
159}
160
161void RemoteFontFaceSource::FontLoadHistograms::recordRemoteFont(const FontResource* font)
162{
163    if (m_loadStartTime > 0 && font && !font->isLoading()) {
164        int duration = static_cast<int>(currentTimeMS() - m_loadStartTime);
165        blink::Platform::current()->histogramCustomCounts(histogramName(font), duration, 0, 10000, 50);
166        m_loadStartTime = -1;
167
168        enum { Miss, Hit, DataUrl, CacheHitEnumMax };
169        int histogramValue = font->url().protocolIsData() ? DataUrl
170            : font->response().wasCached() ? Hit
171            : Miss;
172        blink::Platform::current()->histogramEnumeration("WebFont.CacheHit", histogramValue, CacheHitEnumMax);
173
174        enum { CORSFail, CORSSuccess, CORSEnumMax };
175        int corsValue = font->isCORSFailed() ? CORSFail : CORSSuccess;
176        blink::Platform::current()->histogramEnumeration("WebFont.CORSSuccess", corsValue, CORSEnumMax);
177    }
178}
179
180const char* RemoteFontFaceSource::FontLoadHistograms::histogramName(const FontResource* font)
181{
182    if (font->errorOccurred())
183        return "WebFont.DownloadTime.LoadError";
184
185    unsigned size = font->encodedSize();
186    if (size < 10 * 1024)
187        return "WebFont.DownloadTime.0.Under10KB";
188    if (size < 50 * 1024)
189        return "WebFont.DownloadTime.1.10KBTo50KB";
190    if (size < 100 * 1024)
191        return "WebFont.DownloadTime.2.50KBTo100KB";
192    if (size < 1024 * 1024)
193        return "WebFont.DownloadTime.3.100KBTo1MB";
194    return "WebFont.DownloadTime.4.Over1MB";
195}
196
197} // namespace blink
198