Home | History | Annotate | Download | only in css
      1 /*
      2  * Copyright (C) 2007, 2008, 2010, 2011 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "core/css/CSSFontFaceSource.h"
     28 
     29 #include "RuntimeEnabledFeatures.h"
     30 #include "core/css/CSSCustomFontData.h"
     31 #include "core/css/CSSFontFace.h"
     32 #include "platform/fonts/FontCache.h"
     33 #include "platform/fonts/FontDescription.h"
     34 #include "platform/fonts/SimpleFontData.h"
     35 #include "public/platform/Platform.h"
     36 #include "wtf/CurrentTime.h"
     37 
     38 #if ENABLE(SVG_FONTS)
     39 #include "SVGNames.h"
     40 #include "core/svg/SVGFontData.h"
     41 #include "core/svg/SVGFontElement.h"
     42 #include "core/svg/SVGFontFaceElement.h"
     43 #endif
     44 
     45 namespace WebCore {
     46 
     47 CSSFontFaceSource::CSSFontFaceSource(const String& str, FontResource* font)
     48     : m_string(str)
     49     , m_font(font)
     50     , m_face(0)
     51 #if ENABLE(SVG_FONTS)
     52     , m_hasExternalSVGFont(false)
     53 #endif
     54 {
     55     if (m_font)
     56         m_font->addClient(this);
     57 }
     58 
     59 CSSFontFaceSource::~CSSFontFaceSource()
     60 {
     61     if (m_font)
     62         m_font->removeClient(this);
     63     pruneTable();
     64 }
     65 
     66 void CSSFontFaceSource::pruneTable()
     67 {
     68     if (m_fontDataTable.isEmpty())
     69         return;
     70 
     71     for (FontDataTable::iterator it = m_fontDataTable.begin(); it != m_fontDataTable.end(); ++it) {
     72         SimpleFontData* fontData = it->value.get();
     73         if (fontData && fontData->customFontData())
     74             fontData->customFontData()->clearCSSFontFaceSource();
     75     }
     76     m_fontDataTable.clear();
     77 }
     78 
     79 bool CSSFontFaceSource::isLocal() const
     80 {
     81     if (m_font)
     82         return false;
     83 #if ENABLE(SVG_FONTS)
     84     if (m_svgFontFaceElement)
     85         return false;
     86 #endif
     87     return true;
     88 }
     89 
     90 bool CSSFontFaceSource::isLoading() const
     91 {
     92     if (m_font)
     93         return !m_font->stillNeedsLoad() && !m_font->isLoaded();
     94     return false;
     95 }
     96 
     97 bool CSSFontFaceSource::isLoaded() const
     98 {
     99     if (m_font)
    100         return m_font->isLoaded();
    101     return true;
    102 }
    103 
    104 bool CSSFontFaceSource::isValid() const
    105 {
    106     if (m_font)
    107         return !m_font->errorOccurred();
    108     return true;
    109 }
    110 
    111 void CSSFontFaceSource::didStartFontLoad(FontResource*)
    112 {
    113     // Avoid duplicated reports when multiple CSSFontFaceSource are registered
    114     // at this FontResource.
    115     if (!m_fontDataTable.isEmpty())
    116         m_histograms.loadStarted();
    117 }
    118 
    119 void CSSFontFaceSource::fontLoaded(FontResource*)
    120 {
    121     if (!m_fontDataTable.isEmpty())
    122         m_histograms.recordRemoteFont(m_font.get());
    123 
    124     pruneTable();
    125     if (m_face)
    126         m_face->fontLoaded(this);
    127 }
    128 
    129 PassRefPtr<SimpleFontData> CSSFontFaceSource::getFontData(const FontDescription& fontDescription)
    130 {
    131     // If the font hasn't loaded or an error occurred, then we've got nothing.
    132     if (!isValid())
    133         return 0;
    134 
    135     if (isLocal()) {
    136         // We're local. Just return a SimpleFontData from the normal cache.
    137         // We don't want to check alternate font family names here, so pass true as the checkingAlternateName parameter.
    138         RefPtr<SimpleFontData> fontData = FontCache::fontCache()->getFontData(fontDescription, m_string, true);
    139         m_histograms.recordLocalFont(fontData);
    140         return fontData;
    141     }
    142 
    143     // See if we have a mapping in our FontData cache.
    144     AtomicString emptyFontFamily = "";
    145     FontCacheKey key = fontDescription.cacheKey(emptyFontFamily);
    146 
    147     RefPtr<SimpleFontData>& fontData = m_fontDataTable.add(key.hash(), 0).iterator->value;
    148     if (fontData)
    149         return fontData; // No release, because fontData is a reference to a RefPtr that is held in the m_fontDataTable.
    150 
    151     // If we are still loading, then we let the system pick a font.
    152     if (isLoaded()) {
    153         if (m_font) {
    154 #if ENABLE(SVG_FONTS)
    155             if (m_hasExternalSVGFont) {
    156                 // For SVG fonts parse the external SVG document, and extract the <font> element.
    157                 if (!m_font->ensureSVGFontData())
    158                     return 0;
    159 
    160                 if (!m_externalSVGFontElement) {
    161                     String fragmentIdentifier;
    162                     size_t start = m_string.find('#');
    163                     if (start != kNotFound)
    164                         fragmentIdentifier = m_string.string().substring(start + 1);
    165                     m_externalSVGFontElement = m_font->getSVGFontById(fragmentIdentifier);
    166                 }
    167 
    168                 if (!m_externalSVGFontElement)
    169                     return 0;
    170 
    171                 SVGFontFaceElement* fontFaceElement = 0;
    172 
    173                 // Select first <font-face> child
    174                 for (Node* fontChild = m_externalSVGFontElement->firstChild(); fontChild; fontChild = fontChild->nextSibling()) {
    175                     if (fontChild->hasTagName(SVGNames::font_faceTag)) {
    176                         fontFaceElement = toSVGFontFaceElement(fontChild);
    177                         break;
    178                     }
    179                 }
    180 
    181                 if (fontFaceElement) {
    182                     if (!m_svgFontFaceElement) {
    183                         // We're created using a CSS @font-face rule, that means we're not associated with a SVGFontFaceElement.
    184                         // Use the imported <font-face> tag as referencing font-face element for these cases.
    185                         m_svgFontFaceElement = fontFaceElement;
    186                     }
    187 
    188                     fontData = SimpleFontData::create(
    189                         SVGFontData::create(fontFaceElement),
    190                         fontDescription.effectiveFontSize(),
    191                         fontDescription.isSyntheticBold(),
    192                         fontDescription.isSyntheticItalic());
    193                 }
    194             } else
    195 #endif
    196             {
    197                 // Create new FontPlatformData from our CGFontRef, point size and ATSFontRef.
    198                 if (!m_font->ensureCustomFontData())
    199                     return 0;
    200 
    201                 fontData = SimpleFontData::create(
    202                     m_font->platformDataFromCustomData(fontDescription.effectiveFontSize(),
    203                         fontDescription.isSyntheticBold(), fontDescription.isSyntheticItalic(),
    204                         fontDescription.orientation(), fontDescription.widthVariant()), CustomFontData::create(false));
    205             }
    206         } else {
    207 #if ENABLE(SVG_FONTS)
    208             // In-Document SVG Fonts
    209             if (m_svgFontFaceElement) {
    210                 fontData = SimpleFontData::create(
    211                     SVGFontData::create(m_svgFontFaceElement.get()),
    212                     fontDescription.effectiveFontSize(),
    213                     fontDescription.isSyntheticBold(),
    214                     fontDescription.isSyntheticItalic());
    215             }
    216 #endif
    217         }
    218     } else {
    219         // This temporary font is not retained and should not be returned.
    220         FontCachePurgePreventer fontCachePurgePreventer;
    221         SimpleFontData* temporaryFont = FontCache::fontCache()->getNonRetainedLastResortFallbackFont(fontDescription);
    222         RefPtr<CSSCustomFontData> cssFontData = CSSCustomFontData::create(true);
    223         cssFontData->setCSSFontFaceSource(this);
    224         fontData = SimpleFontData::create(temporaryFont->platformData(), cssFontData);
    225     }
    226 
    227     return fontData; // No release, because fontData is a reference to a RefPtr that is held in the m_fontDataTable.
    228 }
    229 
    230 #if ENABLE(SVG_FONTS)
    231 SVGFontFaceElement* CSSFontFaceSource::svgFontFaceElement() const
    232 {
    233     return m_svgFontFaceElement.get();
    234 }
    235 
    236 void CSSFontFaceSource::setSVGFontFaceElement(PassRefPtr<SVGFontFaceElement> element)
    237 {
    238     m_svgFontFaceElement = element;
    239 }
    240 
    241 bool CSSFontFaceSource::isSVGFontFaceSource() const
    242 {
    243     return m_svgFontFaceElement || m_hasExternalSVGFont;
    244 }
    245 #endif
    246 
    247 bool CSSFontFaceSource::ensureFontData()
    248 {
    249     if (!m_font)
    250         return false;
    251 #if ENABLE(SVG_FONTS)
    252     if (m_hasExternalSVGFont)
    253         return m_font->ensureSVGFontData();
    254 #endif
    255     return m_font->ensureCustomFontData();
    256 }
    257 
    258 bool CSSFontFaceSource::isLocalFontAvailable(const FontDescription& fontDescription)
    259 {
    260     if (!isLocal())
    261         return false;
    262     return FontCache::fontCache()->isPlatformFontAvailable(fontDescription, m_string);
    263 }
    264 
    265 void CSSFontFaceSource::beginLoadIfNeeded()
    266 {
    267     if (m_face && m_font)
    268         m_face->beginLoadIfNeeded(this);
    269 }
    270 
    271 void CSSFontFaceSource::FontLoadHistograms::loadStarted()
    272 {
    273     if (!m_loadStartTime)
    274         m_loadStartTime = currentTimeMS();
    275 }
    276 
    277 void CSSFontFaceSource::FontLoadHistograms::recordLocalFont(bool loadSuccess)
    278 {
    279     if (!m_loadStartTime) {
    280         blink::Platform::current()->histogramEnumeration("WebFont.LocalFontUsed", loadSuccess ? 1 : 0, 2);
    281         m_loadStartTime = -1; // Do not count this font again.
    282     }
    283 }
    284 
    285 void CSSFontFaceSource::FontLoadHistograms::recordRemoteFont(const FontResource* font)
    286 {
    287     if (m_loadStartTime > 0 && font && !font->isLoading()) {
    288         int duration = static_cast<int>(currentTimeMS() - m_loadStartTime);
    289         blink::Platform::current()->histogramCustomCounts(histogramName(font), duration, 0, 10000, 50);
    290         m_loadStartTime = -1;
    291 
    292         enum { Miss, Hit, DataUrl, CacheHitEnumMax };
    293         int histogramValue = font->url().protocolIsData() ? DataUrl
    294             : font->response().wasCached() ? Hit
    295             : Miss;
    296         blink::Platform::current()->histogramEnumeration("WebFont.CacheHit", histogramValue, CacheHitEnumMax);
    297     }
    298 }
    299 
    300 const char* CSSFontFaceSource::FontLoadHistograms::histogramName(const FontResource* font)
    301 {
    302     if (font->errorOccurred())
    303         return "WebFont.DownloadTime.LoadError";
    304 
    305     unsigned size = font->encodedSize();
    306     if (size < 10 * 1024)
    307         return "WebFont.DownloadTime.0.Under10KB";
    308     if (size < 50 * 1024)
    309         return "WebFont.DownloadTime.1.10KBTo50KB";
    310     if (size < 100 * 1024)
    311         return "WebFont.DownloadTime.2.50KBTo100KB";
    312     if (size < 1024 * 1024)
    313         return "WebFont.DownloadTime.3.100KBTo1MB";
    314     return "WebFont.DownloadTime.4.Over1MB";
    315 }
    316 
    317 }
    318